github.com/oam-dev/kubevela@v1.9.11/pkg/controller/core.oam.dev/v1beta1/application/suite_test.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package application
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"math/rand"
    23  	"os"
    24  	"path/filepath"
    25  	"strconv"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/crossplane/crossplane-runtime/pkg/event"
    30  	terraformv1beta2 "github.com/oam-dev/terraform-controller/api/v1beta2"
    31  	. "github.com/onsi/ginkgo/v2"
    32  	. "github.com/onsi/gomega"
    33  	"github.com/pkg/errors"
    34  	corev1 "k8s.io/api/core/v1"
    35  	crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    36  	"k8s.io/apimachinery/pkg/api/meta"
    37  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    38  	"k8s.io/apimachinery/pkg/runtime"
    39  	"k8s.io/client-go/kubernetes/scheme"
    40  	"k8s.io/client-go/rest"
    41  	"k8s.io/utils/pointer"
    42  	ctrl "sigs.k8s.io/controller-runtime"
    43  	"sigs.k8s.io/controller-runtime/pkg/client"
    44  	"sigs.k8s.io/controller-runtime/pkg/envtest"
    45  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    46  	"sigs.k8s.io/controller-runtime/pkg/log/zap"
    47  
    48  	"github.com/kubevela/workflow/pkg/cue/packages"
    49  
    50  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    51  	"github.com/oam-dev/kubevela/pkg/appfile"
    52  	"github.com/oam-dev/kubevela/pkg/multicluster"
    53  	// +kubebuilder:scaffold:imports
    54  )
    55  
    56  // These tests use Ginkgo (BDD-style Go testing framework). Refer to
    57  // http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
    58  var cfg *rest.Config
    59  var recorder = NewFakeRecorder(10000)
    60  var k8sClient client.Client
    61  var testEnv *envtest.Environment
    62  var testScheme = runtime.NewScheme()
    63  var reconciler *Reconciler
    64  var appParser *appfile.Parser
    65  var controllerDone context.CancelFunc
    66  var mgr ctrl.Manager
    67  var appRevisionLimit = 5
    68  
    69  func TestAPIs(t *testing.T) {
    70  	RegisterFailHandler(Fail)
    71  
    72  	RunSpecs(t, "Controller Suite")
    73  }
    74  
    75  var _ = BeforeSuite(func() {
    76  	logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter)))
    77  	rand.Seed(time.Now().UnixNano())
    78  	By("bootstrapping test environment")
    79  	var yamlPath string
    80  	if _, set := os.LookupEnv("COMPATIBILITY_TEST"); set {
    81  		yamlPath = "../../../../../test/compatibility-test/testdata"
    82  	} else {
    83  		yamlPath = filepath.Join("../../../../..", "charts", "vela-core", "crds")
    84  	}
    85  	logf.Log.Info("start application suit test", "yaml_path", yamlPath)
    86  	testEnv = &envtest.Environment{
    87  		ControlPlaneStartTimeout: time.Minute,
    88  		ControlPlaneStopTimeout:  time.Minute,
    89  		UseExistingCluster:       pointer.Bool(false),
    90  		CRDDirectoryPaths:        []string{yamlPath, "./testdata/crds/terraform.core.oam.dev_configurations.yaml"},
    91  	}
    92  
    93  	var err error
    94  	cfg, err = testEnv.Start()
    95  	Expect(err).ToNot(HaveOccurred())
    96  	Expect(cfg).ToNot(BeNil())
    97  
    98  	err = v1beta1.SchemeBuilder.AddToScheme(testScheme)
    99  	Expect(err).NotTo(HaveOccurred())
   100  
   101  	err = scheme.AddToScheme(testScheme)
   102  	Expect(err).NotTo(HaveOccurred())
   103  
   104  	terraformv1beta2.AddToScheme(testScheme)
   105  
   106  	crdv1.AddToScheme(testScheme)
   107  
   108  	// +kubebuilder:scaffold:scheme
   109  	k8sClient, err = client.New(cfg, client.Options{Scheme: testScheme})
   110  	Expect(err).ToNot(HaveOccurred())
   111  	Expect(k8sClient).ToNot(BeNil())
   112  	pd, err := packages.NewPackageDiscover(cfg)
   113  	Expect(err).To(BeNil())
   114  
   115  	appParser = appfile.NewApplicationParser(k8sClient, pd)
   116  
   117  	reconciler = &Reconciler{
   118  		Client:   k8sClient,
   119  		Scheme:   testScheme,
   120  		pd:       pd,
   121  		Recorder: event.NewAPIRecorder(recorder),
   122  	}
   123  
   124  	reconciler.appRevisionLimit = appRevisionLimit
   125  	// setup the controller manager since we need the component handler to run in the background
   126  	mgr, err = ctrl.NewManager(cfg, ctrl.Options{
   127  		Scheme:                  testScheme,
   128  		MetricsBindAddress:      "0",
   129  		LeaderElection:          false,
   130  		LeaderElectionNamespace: "default",
   131  		LeaderElectionID:        "test",
   132  	})
   133  	Expect(err).NotTo(HaveOccurred())
   134  	definitionNs := corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}}
   135  	Expect(k8sClient.Create(context.Background(), definitionNs.DeepCopy())).Should(BeNil())
   136  
   137  	var ctx context.Context
   138  	ctx, controllerDone = context.WithCancel(context.Background())
   139  	// start the controller in the background so that new componentRevisions are created
   140  	go func() {
   141  		err = mgr.Start(ctx)
   142  		Expect(err).NotTo(HaveOccurred())
   143  	}()
   144  	multicluster.InitClusterInfo(cfg)
   145  })
   146  
   147  var _ = AfterSuite(func() {
   148  	By("tearing down the test environment")
   149  	if controllerDone != nil {
   150  		controllerDone()
   151  	}
   152  	err := testEnv.Stop()
   153  	Expect(err).ToNot(HaveOccurred())
   154  })
   155  
   156  type FakeRecorder struct {
   157  	Events  chan string
   158  	Message map[string][]*Events
   159  }
   160  
   161  type Events struct {
   162  	Name      string
   163  	Namespace string
   164  	EventType string
   165  	Reason    string
   166  	Message   string
   167  }
   168  
   169  func (f *FakeRecorder) Event(object runtime.Object, eventtype, reason, message string) {
   170  	if f.Events != nil {
   171  		objectMeta, err := meta.Accessor(object)
   172  		if err != nil {
   173  			return
   174  		}
   175  
   176  		event := &Events{
   177  			Name:      objectMeta.GetName(),
   178  			Namespace: objectMeta.GetNamespace(),
   179  			EventType: eventtype,
   180  			Reason:    reason,
   181  			Message:   message,
   182  		}
   183  
   184  		records, ok := f.Message[objectMeta.GetName()]
   185  		if !ok {
   186  			f.Message[objectMeta.GetName()] = []*Events{event}
   187  			return
   188  		}
   189  
   190  		records = append(records, event)
   191  		f.Message[objectMeta.GetName()] = records
   192  
   193  	}
   194  }
   195  
   196  func (f *FakeRecorder) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) {
   197  	f.Event(object, eventtype, reason, messageFmt)
   198  }
   199  
   200  func (f *FakeRecorder) AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) {
   201  	f.Eventf(object, eventtype, reason, messageFmt, args...)
   202  }
   203  
   204  func (f *FakeRecorder) GetEventsWithName(name string) ([]*Events, error) {
   205  	records, ok := f.Message[name]
   206  	if !ok {
   207  		return nil, errors.New("not found events")
   208  	}
   209  
   210  	return records, nil
   211  }
   212  
   213  // NewFakeRecorder creates new fake event recorder with event channel with
   214  // buffer of given size.
   215  func NewFakeRecorder(bufferSize int) *FakeRecorder {
   216  	return &FakeRecorder{
   217  		Events:  make(chan string, bufferSize),
   218  		Message: make(map[string][]*Events),
   219  	}
   220  }
   221  
   222  // randomNamespaceName generates a random name based on the basic name.
   223  // Running each ginkgo case in a new namespace with a random name can avoid
   224  // waiting a long time to GC namespace.
   225  func randomNamespaceName(basic string) string {
   226  	return fmt.Sprintf("%s-%s", basic, strconv.FormatInt(rand.Int63(), 16))
   227  }