Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/service-waiter/cmd/component.go
2498 views
1
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package cmd
6
7
import (
8
"context"
9
"fmt"
10
"time"
11
12
"github.com/sirupsen/logrus"
13
"github.com/spf13/cobra"
14
"github.com/spf13/viper"
15
16
k8s "github.com/gitpod-io/gitpod/common-go/kubernetes"
17
"github.com/gitpod-io/gitpod/common-go/log"
18
corev1 "k8s.io/api/core/v1"
19
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20
"k8s.io/client-go/kubernetes"
21
"k8s.io/client-go/rest"
22
)
23
24
var componentCmdOpt struct {
25
image string
26
namespace string
27
component string
28
labels string
29
}
30
31
var componentCmd = &cobra.Command{
32
Use: "component",
33
Short: "waits for component to become latest build of current installer build",
34
PreRun: func(cmd *cobra.Command, args []string) {
35
err := viper.BindPFlags(cmd.Flags())
36
if err != nil {
37
log.WithError(err).Fatal("cannot bind Viper to pflags")
38
}
39
},
40
Run: func(cmd *cobra.Command, args []string) {
41
if componentCmdOpt.image == "" {
42
log.Errorf("target image is empty, skip service waiter %s", componentCmdOpt.component)
43
return
44
}
45
timeout := getTimeout()
46
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")
47
ctx, cancel := context.WithTimeout(cmd.Context(), timeout)
48
defer cancel()
49
50
err := waitPodsImage(ctx)
51
if err != nil {
52
log.WithError(err).Fatal("failed to wait service")
53
} else {
54
log.Info("service is ready")
55
}
56
},
57
}
58
59
func checkPodsImage(ctx context.Context, k8sClient *kubernetes.Clientset) (bool, error) {
60
pods, err := k8sClient.CoreV1().Pods(componentCmdOpt.namespace).List(ctx, metav1.ListOptions{
61
LabelSelector: componentCmdOpt.labels,
62
})
63
if err != nil {
64
return false, fmt.Errorf("cannot get pod list: %w", err)
65
}
66
if len(pods.Items) == 0 {
67
return false, fmt.Errorf("no pods found")
68
}
69
readyCount := 0
70
for _, pod := range pods.Items {
71
if pod.Annotations[k8s.ImageNameAnnotation] != componentCmdOpt.image {
72
return false, fmt.Errorf("image is not the same: %s != %s", pod.Annotations[k8s.ImageNameAnnotation], componentCmdOpt.image)
73
}
74
for _, condition := range pod.Status.Conditions {
75
if condition.Type == corev1.PodReady {
76
if condition.Status == corev1.ConditionTrue {
77
readyCount += 1
78
} else {
79
return false, fmt.Errorf("pod is not ready")
80
}
81
}
82
}
83
}
84
log.Infof("ready pods: %d/%d", readyCount, len(pods.Items))
85
return readyCount == len(pods.Items), nil
86
}
87
88
func waitPodsImage(ctx context.Context) error {
89
k8sCfg, err := rest.InClusterConfig()
90
if err != nil {
91
return fmt.Errorf("cannot get in cluster config: %w", err)
92
}
93
k8sClient, err := kubernetes.NewForConfig(k8sCfg)
94
if err != nil {
95
return fmt.Errorf("cannot create k8s client: %w", err)
96
}
97
ok := false
98
for {
99
select {
100
case <-ctx.Done():
101
if ok {
102
return nil
103
}
104
return ctx.Err()
105
default:
106
ok, err := checkPodsImage(ctx, k8sClient)
107
if err != nil {
108
log.WithError(err).Error("image check failed")
109
time.Sleep(1 * time.Second)
110
continue
111
}
112
if ok {
113
return nil
114
}
115
time.Sleep(1 * time.Second)
116
}
117
}
118
}
119
120
func init() {
121
rootCmd.AddCommand(componentCmd)
122
componentCmd.Flags().StringVar(&componentCmdOpt.image, "image", "", "The latest image of current installer build")
123
componentCmd.Flags().StringVar(&componentCmdOpt.namespace, "namespace", "", "The namespace of deployment")
124
componentCmd.Flags().StringVar(&componentCmdOpt.component, "component", "", "Component name of deployment")
125
componentCmd.Flags().StringVar(&componentCmdOpt.labels, "labels", "", "Labels of deployment")
126
127
_ = componentCmd.MarkFlagRequired("namespace")
128
_ = componentCmd.MarkFlagRequired("component")
129
_ = componentCmd.MarkFlagRequired("labels")
130
}
131
132