// Copyright (c) 2021 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 integration import ( "context" "flag" "fmt" "math/rand" "os" "testing" "time" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "k8s.io/client-go/tools/clientcmd" "k8s.io/klog/v2" "sigs.k8s.io/e2e-framework/klient" "sigs.k8s.io/e2e-framework/pkg/env" "sigs.k8s.io/e2e-framework/pkg/envconf" "sigs.k8s.io/e2e-framework/pkg/flags" ) func SkipWithoutUsername(t *testing.T, username string) { if username == "" { t.Skip("Skipping because requires a username") } } func SkipWithoutUserToken(t *testing.T, userToken string) { if userToken == "" { t.Skip("Skipping because requires a user token") } } func SkipWithoutEnterpriseLicense(t *testing.T, enterpise bool) { if !enterpise { t.Skip("Skipping because requires enterprise license") } } func EnsureUserExists(t *testing.T, username string, api *ComponentAPI) string { if username == "" { t.Logf("no username provided, creating temporary one") rand.Seed(time.Now().UnixNano()) randN := rand.Intn(1000) newUser := fmt.Sprintf("johndoe%d", randN) userId, err := CreateUser(newUser, false, api) if err != nil { t.Fatalf("cannot create user: %q", err) } t.Cleanup(func() { err := DeleteUser(userId, api) if err != nil { t.Fatalf("error deleting user %q", err) } }) t.Logf("user '%s' with ID %s created", newUser, userId) return newUser } return username } func Setup(ctx context.Context) (string, string, env.Environment, bool, string, bool) { var ( username string enterprise bool gitlab bool waitGitpodReady time.Duration namespace string kubeconfig string feature string assess string parallel bool labels = make(flags.LabelsMap) ) flagset := flag.CommandLine klog.InitFlags(flagset) flagset.StringVar(&username, "username", "", "username to execute the tests with. Chooses one automatically if left blank.") flagset.BoolVar(&enterprise, "enterprise", false, "whether to test enterprise features. requires enterprise lisence installed.") flagset.BoolVar(&gitlab, "gitlab", false, "whether to test gitlab integration.") flagset.BoolVar(¶llel, "parallel", false, "Run test features in parallel") flagset.DurationVar(&waitGitpodReady, "wait-gitpod-timeout", 5*time.Minute, `wait time for Gitpod components before starting integration test`) flagset.StringVar(&namespace, "namespace", "", "Kubernetes cluster namespaces to use") flagset.StringVar(&kubeconfig, "kubeconfig", "", "The path to the kubeconfig file") flagset.StringVar(&feature, "feature", "", "Regular expression that targets features to test") flagset.StringVar(&assess, "assess", "", "Regular expression that targets assertive steps to run") flagset.Var(&labels, "labels", "Comma-separated key/value pairs to filter tests by labels") if err := flagset.Parse(os.Args[1:]); err != nil { klog.Fatalf("cannot parse flags: %v", err) } e := envconf.New() if assess != "" { e.WithAssessmentRegex(assess) } if feature != "" { e.WithFeatureRegex(feature) } if parallel { e.WithParallelTestEnabled() } client, err := klient.NewWithKubeConfigFile(kubeconfig) if err != nil { klog.Fatalf("unexpected error: %v", err) } e.WithClient(client) e.WithLabels(labels) e.WithNamespace(namespace) // use the namespace from the CurrentContext if namespace == "" { ns, err := getNamespace(kubeconfig) if err != nil { klog.Fatalf("unexpected error obtaining context namespace: %v", err) } e.WithNamespace(ns) } testenv, err := env.NewWithContext(ctx, e) if err != nil { klog.Fatalf("unexpected error: %v", err) } testenv.Setup( waitOnGitpodRunning(e.Namespace(), waitGitpodReady), ) return username, e.Namespace(), testenv, enterprise, kubeconfig, gitlab } func waitOnGitpodRunning(namespace string, waitTimeout time.Duration) env.Func { klog.V(2).Info("Checking status of Gitpod components...") return func(ctx context.Context, cfg *envconf.Config) (context.Context, error) { components := []string{ "agent-smith", "blobserve", "content-service", "dashboard", "image-builder-mk3", "proxy", "registry-facade", "server", "ws-daemon", "ws-manager", "ws-manager-bridge", "ws-proxy", } client := cfg.Client() err := wait.PollImmediate(5*time.Second, waitTimeout, func() (bool, error) { for _, component := range components { var pods corev1.PodList err := client.Resources(namespace).List(context.Background(), &pods, func(opts *metav1.ListOptions) { opts.LabelSelector = fmt.Sprintf("component=%v", component) }) if err != nil { klog.Errorf("unexpected error searching Gitpod components: %v", err) return false, nil } if len(pods.Items) == 0 { klog.Warningf("no pod ready for component %v", component) return false, nil } for _, p := range pods.Items { var isReady bool for _, cond := range p.Status.Conditions { if cond.Type == corev1.PodReady { isReady = cond.Status == corev1.ConditionTrue break } } if !isReady { klog.Warningf("no pod ready for component %v", component) return false, nil } } } klog.V(2).Info("All Gitpod components are running...") return true, nil }) if err != nil { return ctx, nil } return ctx, nil } } func getNamespace(path string) (string, error) { var cfg clientcmd.ClientConfig switch path { case "": loadingrules := clientcmd.NewDefaultClientConfigLoadingRules() cfg = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingrules, &clientcmd.ConfigOverrides{}) default: cfg = clientcmd.NewNonInteractiveDeferredLoadingClientConfig( &clientcmd.ClientConfigLoadingRules{ExplicitPath: path}, &clientcmd.ConfigOverrides{}) } namespace, _, err := cfg.Namespace() if err != nil { return "", err } return namespace, nil }