Skip to content

Make sure dashboard is deployed after server and public api server #18995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions components/service-waiter/cmd/component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.

package cmd

import (
"context"
"fmt"
"time"

"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/gitpod-io/gitpod/common-go/log"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)

var componentCmdOpt struct {
image string
namespace string
component string
labels string
}

var componentCmd = &cobra.Command{
Use: "component",
Short: "waits for component to become latest build of current installer build",
PreRun: func(cmd *cobra.Command, args []string) {
err := viper.BindPFlags(cmd.Flags())
if err != nil {
log.WithError(err).Fatal("cannot bind Viper to pflags")
}
},
Run: func(cmd *cobra.Command, args []string) {
if componentCmdOpt.image == "" {
log.Errorf("target image is empty, skip service waiter %s", componentCmdOpt.component)
return
}
timeout := getTimeout()
log.WithField("timeout", timeout.String()).WithFields(logrus.Fields{"image": componentCmdOpt.image, "component": componentCmdOpt.component, "namespace": componentCmdOpt.namespace, "labels": componentCmdOpt.labels}).Info("start to wait component")
ctx, cancel := context.WithTimeout(cmd.Context(), timeout)
defer cancel()

err := waitPodsImage(ctx)

if err != nil {
log.WithError(err).Fatal("failed to wait service")
} else {
log.Info("service is ready")
}
},
}

func checkPodsImage(ctx context.Context, k8sClient *kubernetes.Clientset) (bool, error) {
pods, err := k8sClient.CoreV1().Pods(componentCmdOpt.namespace).List(ctx, metav1.ListOptions{
LabelSelector: componentCmdOpt.labels,
})
if err != nil {
return false, fmt.Errorf("cannot get pod list: %w", err)
}
if len(pods.Items) == 0 {
return false, fmt.Errorf("no pods found")
}
readyCount := 0
for _, pod := range pods.Items {
for _, container := range pod.Spec.Containers {
if container.Name == componentCmdOpt.component {
if container.Image != componentCmdOpt.image {
return false, fmt.Errorf("image is not the same: %s != %s", container.Image, componentCmdOpt.image)
}
for _, condition := range pod.Status.Conditions {
if condition.Type == corev1.PodReady {
if condition.Status == corev1.ConditionTrue {
readyCount += 1
} else {
return false, fmt.Errorf("pod is not ready")
}
}
}
}
}
}
log.Infof("ready pods: %d/%d", readyCount, len(pods.Items))
return readyCount == len(pods.Items), nil
}

func waitPodsImage(ctx context.Context) error {
k8sCfg, err := rest.InClusterConfig()
if err != nil {
return fmt.Errorf("cannot get in cluster config: %w", err)
}
k8sClient, err := kubernetes.NewForConfig(k8sCfg)
if err != nil {
return fmt.Errorf("cannot create k8s client: %w", err)
}
ok := false
for {
select {
case <-ctx.Done():
if ok {
return nil
}
return ctx.Err()
default:
ok, err := checkPodsImage(ctx, k8sClient)
if err != nil {
log.WithError(err).Error("image check failed")
time.Sleep(1 * time.Second)
continue
}
if ok {
return nil
}
time.Sleep(1 * time.Second)
}
}
}

func init() {
rootCmd.AddCommand(componentCmd)
componentCmd.Flags().StringVar(&componentCmdOpt.image, "image", "", "The latest image of current installer build")
componentCmd.Flags().StringVar(&componentCmdOpt.namespace, "namespace", "", "The namespace of deployment")
componentCmd.Flags().StringVar(&componentCmdOpt.component, "component", "", "Component name of deployment")
componentCmd.Flags().StringVar(&componentCmdOpt.labels, "labels", "", "Labels of deployment")

_ = componentCmd.MarkFlagRequired("namespace")
_ = componentCmd.MarkFlagRequired("component")
_ = componentCmd.MarkFlagRequired("labels")
}
35 changes: 34 additions & 1 deletion components/service-waiter/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,70 @@ require (
github.com/go-sql-driver/mysql v1.6.0
github.com/mitchellh/go-homedir v1.1.0
github.com/redis/go-redis/v9 v9.0.5
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.4.0
github.com/spf13/viper v1.7.0
k8s.io/api v0.27.3
k8s.io/apimachinery v0.27.3
k8s.io/client-go v0.0.0-00010101000000-000000000000
)

require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gitpod-io/gitpod/components/scrubber v0.0.0-00010101000000-000000000000 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.1 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/mapstructure v1.1.2 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/spf13/afero v1.1.2 // indirect
github.com/spf13/cast v1.3.0 // indirect
github.com/spf13/jwalterweatherman v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/term v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
google.golang.org/grpc v1.52.3 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.51.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

replace github.com/gitpod-io/gitpod/common-go => ../common-go // leeway
Expand Down
Loading