Path: blob/main/components/ws-manager-mk2/controllers/suite_test.go
2498 views
// Copyright (c) 2022 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 controllers56import (7"context"8"path/filepath"9"testing"10"time"1112. "github.com/onsi/ginkgo/v2"13. "github.com/onsi/gomega"14corev1 "k8s.io/api/core/v1"15metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"16"k8s.io/client-go/kubernetes/scheme"17ctrl "sigs.k8s.io/controller-runtime"18"sigs.k8s.io/controller-runtime/pkg/client"19"sigs.k8s.io/controller-runtime/pkg/envtest"20logf "sigs.k8s.io/controller-runtime/pkg/log"21"sigs.k8s.io/controller-runtime/pkg/log/zap"22"sigs.k8s.io/controller-runtime/pkg/metrics"2324"github.com/gitpod-io/gitpod/common-go/util"25"github.com/gitpod-io/gitpod/ws-manager/api/config"26workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1"27//+kubebuilder:scaffold:imports28)2930// These tests use Ginkgo (BDD-style Go testing framework). Refer to31// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.3233const (34timeout = time.Second * 2035duration = time.Second * 236interval = time.Millisecond * 25037secretsNamespace = "workspace-secrets"38)3940// var cfg *rest.Config41var k8sClient client.Client42var testEnv *envtest.Environment4344func TestAPIs(t *testing.T) {45RegisterFailHandler(Fail)4647RunSpecs(t, "Controller Suite")48}4950var (51ctx context.Context52cancel context.CancelFunc53wsMetrics *controllerMetrics54RegisterSubscriber func(func(*workspacev1.Workspace))55)5657var _ = BeforeSuite(func() {58logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))5960By("bootstrapping test environment")61testEnv = &envtest.Environment{62ControlPlaneStartTimeout: 1 * time.Minute,63ControlPlaneStopTimeout: 1 * time.Minute,64CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},65ErrorIfCRDPathMissing: true,66}6768cfg, err := testEnv.Start()69Expect(err).NotTo(HaveOccurred())70Expect(cfg).NotTo(BeNil())7172err = workspacev1.AddToScheme(scheme.Scheme)73Expect(err).NotTo(HaveOccurred())7475//+kubebuilder:scaffold:scheme7677k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})78Expect(err).NotTo(HaveOccurred())79Expect(k8sClient).NotTo(BeNil())8081/*82One thing that this autogenerated file is missing, however, is a way to actually start your controller.83The code above will set up a client for interacting with your custom Kind,84but will not be able to test your controller behavior.85If you want to test your custom controller logic, you’ll need to add some familiar-looking manager logic86to your BeforeSuite() function, so you can register your custom controller to run on this test cluster.87You may notice that the code below runs your controller with nearly identical logic to your CronJob project’s main.go!88The only difference is that the manager is started in a separate goroutine so it does not block the cleanup of envtest89when you’re done running your tests.90Note that we set up both a "live" k8s client and a separate client from the manager. This is because when making91assertions in tests, you generally want to assert against the live state of the API server. If you use the client92from the manager (`k8sManager.GetClient`), you'd end up asserting against the contents of the cache instead, which is93slower and can introduce flakiness into your tests. We could use the manager's `APIReader` to accomplish the same94thing, but that would leave us with two clients in our test assertions and setup (one for reading, one for writing),95and it'd be easy to make mistakes.96Note that we keep the reconciler running against the manager's cache client, though -- we want our controller to97behave as it would in production, and we use features of the cache (like indicies) in our controller which aren't98available when talking directly to the API server.99*/100k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{101Scheme: scheme.Scheme,102})103Expect(err).ToNot(HaveOccurred())104105SetupIndexer(k8sManager)106107conf := newTestConfig()108maintenance := &fakeMaintenance{enabled: false}109wsReconciler, err := NewWorkspaceReconciler(k8sManager.GetClient(), k8sManager.GetConfig(), k8sManager.GetScheme(), k8sManager.GetEventRecorderFor("workspace"), &conf, metrics.Registry, maintenance)110wsMetrics = wsReconciler.metrics111Expect(err).ToNot(HaveOccurred())112Expect(wsReconciler.SetupWithManager(k8sManager)).To(Succeed())113114timeoutReconciler, err := NewTimeoutReconciler(k8sManager.GetClient(), k8sManager.GetEventRecorderFor("workspace"), conf, maintenance)115Expect(err).ToNot(HaveOccurred())116Expect(timeoutReconciler.SetupWithManager(k8sManager)).To(Succeed())117118ctx, cancel = context.WithCancel(context.Background())119subscriberReconciler, err := NewSubscriberReconciler(k8sManager.GetClient(), &conf)120Expect(err).ToNot(HaveOccurred())121Expect(subscriberReconciler.SetupWithManager(ctx, k8sManager)).To(Succeed())122RegisterSubscriber = func(onReconcile func(*workspacev1.Workspace)) {123subscriberReconciler.OnReconcile = func(ctx context.Context, ws *workspacev1.Workspace) {124onReconcile(ws)125}126}127128_ = createNamespace(secretsNamespace)129130go func() {131defer GinkgoRecover()132err := k8sManager.Start(ctx)133Expect(err).ToNot(HaveOccurred(), "failed to run manager")134}()135136})137138func newTestConfig() config.Configuration {139return config.Configuration{140GitpodHostURL: "gitpod.io",141HeartbeatInterval: util.Duration(30 * time.Second),142Namespace: "default",143SecretsNamespace: secretsNamespace,144SeccompProfile: "default.json",145Timeouts: config.WorkspaceTimeoutConfiguration{146AfterClose: util.Duration(1 * time.Minute),147Initialization: util.Duration(30 * time.Minute),148TotalStartup: util.Duration(45 * time.Minute),149RegularWorkspace: util.Duration(60 * time.Minute),150MaxLifetime: util.Duration(36 * time.Hour),151HeadlessWorkspace: util.Duration(90 * time.Minute),152Stopping: util.Duration(60 * time.Minute),153ContentFinalization: util.Duration(55 * time.Minute),154Interrupted: util.Duration(5 * time.Minute),155},156WorkspaceClasses: map[string]*config.WorkspaceClass{157"default": {158Name: "default",159},160},161WorkspaceURLTemplate: "{{ .ID }}-{{ .Prefix }}-{{ .Host }}",162PodRecreationMaxRetries: 3,163PodRecreationBackoff: util.Duration(500 * time.Millisecond),164}165}166167type fakeMaintenance struct {168enabled bool169}170171func (f *fakeMaintenance) IsEnabled(context.Context) bool {172return f.enabled173}174175func createNamespace(name string) *corev1.Namespace {176GinkgoHelper()177178namespace := &corev1.Namespace{179ObjectMeta: metav1.ObjectMeta{180Name: name,181},182}183184Expect(k8sClient.Create(ctx, namespace)).To(Succeed())185return namespace186}187188var _ = AfterSuite(func() {189cancel()190By("tearing down the test environment")191err := testEnv.Stop()192Expect(err).NotTo(HaveOccurred())193})194195196