Path: blob/main/components/service-waiter/cmd/component.go
2498 views
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package cmd56import (7"context"8"fmt"9"time"1011"github.com/sirupsen/logrus"12"github.com/spf13/cobra"13"github.com/spf13/viper"1415k8s "github.com/gitpod-io/gitpod/common-go/kubernetes"16"github.com/gitpod-io/gitpod/common-go/log"17corev1 "k8s.io/api/core/v1"18metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"19"k8s.io/client-go/kubernetes"20"k8s.io/client-go/rest"21)2223var componentCmdOpt struct {24image string25namespace string26component string27labels string28}2930var componentCmd = &cobra.Command{31Use: "component",32Short: "waits for component to become latest build of current installer build",33PreRun: func(cmd *cobra.Command, args []string) {34err := viper.BindPFlags(cmd.Flags())35if err != nil {36log.WithError(err).Fatal("cannot bind Viper to pflags")37}38},39Run: func(cmd *cobra.Command, args []string) {40if componentCmdOpt.image == "" {41log.Errorf("target image is empty, skip service waiter %s", componentCmdOpt.component)42return43}44timeout := getTimeout()45log.WithField("timeout", timeout.String()).WithFields(logrus.Fields{"image": componentCmdOpt.image, "component": componentCmdOpt.component, "namespace": componentCmdOpt.namespace, "labels": componentCmdOpt.labels}).Info("start to wait component")46ctx, cancel := context.WithTimeout(cmd.Context(), timeout)47defer cancel()4849err := waitPodsImage(ctx)50if err != nil {51log.WithError(err).Fatal("failed to wait service")52} else {53log.Info("service is ready")54}55},56}5758func checkPodsImage(ctx context.Context, k8sClient *kubernetes.Clientset) (bool, error) {59pods, err := k8sClient.CoreV1().Pods(componentCmdOpt.namespace).List(ctx, metav1.ListOptions{60LabelSelector: componentCmdOpt.labels,61})62if err != nil {63return false, fmt.Errorf("cannot get pod list: %w", err)64}65if len(pods.Items) == 0 {66return false, fmt.Errorf("no pods found")67}68readyCount := 069for _, pod := range pods.Items {70if pod.Annotations[k8s.ImageNameAnnotation] != componentCmdOpt.image {71return false, fmt.Errorf("image is not the same: %s != %s", pod.Annotations[k8s.ImageNameAnnotation], componentCmdOpt.image)72}73for _, condition := range pod.Status.Conditions {74if condition.Type == corev1.PodReady {75if condition.Status == corev1.ConditionTrue {76readyCount += 177} else {78return false, fmt.Errorf("pod is not ready")79}80}81}82}83log.Infof("ready pods: %d/%d", readyCount, len(pods.Items))84return readyCount == len(pods.Items), nil85}8687func waitPodsImage(ctx context.Context) error {88k8sCfg, err := rest.InClusterConfig()89if err != nil {90return fmt.Errorf("cannot get in cluster config: %w", err)91}92k8sClient, err := kubernetes.NewForConfig(k8sCfg)93if err != nil {94return fmt.Errorf("cannot create k8s client: %w", err)95}96ok := false97for {98select {99case <-ctx.Done():100if ok {101return nil102}103return ctx.Err()104default:105ok, err := checkPodsImage(ctx, k8sClient)106if err != nil {107log.WithError(err).Error("image check failed")108time.Sleep(1 * time.Second)109continue110}111if ok {112return nil113}114time.Sleep(1 * time.Second)115}116}117}118119func init() {120rootCmd.AddCommand(componentCmd)121componentCmd.Flags().StringVar(&componentCmdOpt.image, "image", "", "The latest image of current installer build")122componentCmd.Flags().StringVar(&componentCmdOpt.namespace, "namespace", "", "The namespace of deployment")123componentCmd.Flags().StringVar(&componentCmdOpt.component, "component", "", "Component name of deployment")124componentCmd.Flags().StringVar(&componentCmdOpt.labels, "labels", "", "Labels of deployment")125126_ = componentCmd.MarkFlagRequired("namespace")127_ = componentCmd.MarkFlagRequired("component")128_ = componentCmd.MarkFlagRequired("labels")129}130131132