From b4ae473dae773bc9eb89468466fa8b2c1825cd6a Mon Sep 17 00:00:00 2001 From: Zheng Xi Zhou Date: Thu, 13 May 2021 21:46:45 +0800 Subject: [PATCH] Fix Terraform application status issue (#1611) * Fix Terraform application status issue Fix #1599 * add unit tests * fix import issue --- go.mod | 2 +- go.sum | 4 +- pkg/appfile/appfile.go | 13 +- pkg/appfile/appfile_test.go | 17 ++- .../v1alpha2/application/apply.go | 58 ++++++--- .../v1alpha2/application/apply_test.go | 116 ++++++++++++++++- .../v1alpha2/application/suite_test.go | 17 +-- ...terraform.core.oam.dev_configurations.yaml | 117 ++++++++++++++++++ pkg/utils/common/common.go | 2 + 9 files changed, 303 insertions(+), 43 deletions(-) create mode 100644 pkg/controller/core.oam.dev/v1alpha2/application/testdata/crds/terraform.core.oam.dev_configurations.yaml diff --git a/go.mod b/go.mod index dc8400396..be0ca8559 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/mholt/archiver/v3 v3.3.0 github.com/mitchellh/hashstructure/v2 v2.0.1 github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28 - github.com/oam-dev/terraform-controller v0.1.4 + github.com/oam-dev/terraform-controller v0.1.6 github.com/olekukonko/tablewriter v0.0.2 github.com/onsi/ginkgo v1.13.0 github.com/onsi/gomega v1.10.3 diff --git a/go.sum b/go.sum index 6facfdbcb..a55e74b9c 100644 --- a/go.sum +++ b/go.sum @@ -944,8 +944,8 @@ github.com/oam-dev/stern v1.13.0-alpha h1:EVjM8Qvh6LssB6t4RZrjf9DtCq1cz+/cy6OF7f github.com/oam-dev/stern v1.13.0-alpha/go.mod h1:AOkvfFUv0Arz7GBi0jz7S0Jsu4K/kdvSjNsnRt1+BIg= github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28 h1:tD8HiFKnt0jnwdTWjeqUnfnUYLD/+Nsmj8ZGIxqDWiU= github.com/oam-dev/terraform-config-inspect v0.0.0-20210418082552-fc72d929aa28/go.mod h1:Mu8i0/DdplvnjwRbAYPsc8+LRR27n/mp8VWdkN10GzE= -github.com/oam-dev/terraform-controller v0.1.4 h1:GFd2RqfBZOmCtFzyl/QkB91X1KEfKOs6u6hO+JmlOtI= -github.com/oam-dev/terraform-controller v0.1.4/go.mod h1:8plSKkwgTSWnPDCEaQm/PAwPKed/kVo94Z2wwmJSL6k= +github.com/oam-dev/terraform-controller v0.1.6 h1:Uhd8iMibQ6SNeF5jQI5Z9w7/H7U7XaDwKtNtmbaHTeI= +github.com/oam-dev/terraform-controller v0.1.6/go.mod h1:8plSKkwgTSWnPDCEaQm/PAwPKed/kVo94Z2wwmJSL6k= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= diff --git a/pkg/appfile/appfile.go b/pkg/appfile/appfile.go index ce65ba21a..2f022a4c7 100644 --- a/pkg/appfile/appfile.go +++ b/pkg/appfile/appfile.go @@ -165,11 +165,6 @@ type Appfile struct { Workloads []*Workload } -// TemplateValidate validate Template format -func (af *Appfile) TemplateValidate() error { - return nil -} - // GenerateApplicationConfiguration converts an appFile to applicationConfig & Components func (af *Appfile) GenerateApplicationConfiguration() (*v1alpha2.ApplicationConfiguration, []*v1alpha2.Component, error) { @@ -221,15 +216,15 @@ func (af *Appfile) GenerateApplicationConfiguration() (*v1alpha2.ApplicationConf // PrepareProcessContext prepares a DSL process Context func PrepareProcessContext(wl *Workload, applicationName, revision, namespace string) (process.Context, error) { - pCtx := newContext(wl, applicationName, revision, namespace) + pCtx := NewBasicContext(wl, applicationName, revision, namespace) if err := wl.EvalContext(pCtx); err != nil { return nil, errors.Wrapf(err, "evaluate base template app=%s in namespace=%s", applicationName, namespace) } return pCtx, nil } -// newContext prepares a basic DSL process Context -func newContext(wl *Workload, applicationName, revision, namespace string) process.Context { +// NewBasicContext prepares a basic DSL process Context +func NewBasicContext(wl *Workload, applicationName, revision, namespace string) process.Context { pCtx := process.NewContext(namespace, wl.Name, applicationName, revision) pCtx.InsertSecrets(wl.OutputSecretName, wl.RequiredSecrets) if len(wl.UserConfigs) > 0 { @@ -247,7 +242,7 @@ func generateComponentFromCUEModule(wl *Workload, appName, revision, ns string) } func generateComponentFromTerraformModule(wl *Workload, appName, revision, ns string) (*v1alpha2.Component, *v1alpha2.ApplicationConfigurationComponent, error) { - pCtx := newContext(wl, appName, revision, ns) + pCtx := NewBasicContext(wl, appName, revision, ns) return baseGenerateComponent(pCtx, wl, appName, ns) } diff --git a/pkg/appfile/appfile_test.go b/pkg/appfile/appfile_test.go index 6f9f311f9..f860186be 100644 --- a/pkg/appfile/appfile_test.go +++ b/pkg/appfile/appfile_test.go @@ -29,6 +29,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/pkg/errors" + "gotest.tools/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -38,6 +39,7 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" oamtypes "github.com/oam-dev/kubevela/apis/types" "github.com/oam-dev/kubevela/pkg/dsl/definition" + "github.com/oam-dev/kubevela/pkg/dsl/process" "github.com/oam-dev/kubevela/pkg/oam" "github.com/oam-dev/kubevela/pkg/oam/util" ) @@ -471,6 +473,7 @@ variable "password" { "writeConnectionSecretToRef": map[string]interface{}{ "name": "db", }, + process.OutputSecretName: "db-conn", }, } @@ -805,7 +808,7 @@ variable "password" { revision: "v1", } - pCtx := newContext(args.wl, args.appName, args.revision, ns) + pCtx := NewBasicContext(args.wl, args.appName, args.revision, ns) comp, acc, err = evalWorkloadWithContext(pCtx, args.wl, ns, args.appName, compName) Expect(comp.Spec.Workload).ShouldNot(BeNil()) Expect(acc.ComponentName).Should(Equal("")) @@ -952,3 +955,15 @@ func TestGenerateTerraformConfigurationWorkload(t *testing.T) { } } } + +func TestGetUserConfigName(t *testing.T) { + wl1 := &Workload{Params: nil} + assert.Equal(t, wl1.GetUserConfigName(), "") + + wl2 := &Workload{Params: map[string]interface{}{AppfileBuiltinConfig: 1}} + assert.Equal(t, wl2.GetUserConfigName(), "") + + config := "abc" + wl3 := &Workload{Params: map[string]interface{}{AppfileBuiltinConfig: config}} + assert.Equal(t, wl3.GetUserConfigName(), config) +} diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/apply.go b/pkg/controller/core.oam.dev/v1alpha2/application/apply.go index a88567dc4..06202eab3 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/apply.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/apply.go @@ -27,6 +27,8 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/logging" "github.com/crossplane/crossplane-runtime/pkg/meta" "github.com/go-logr/logr" + terraformtypes "github.com/oam-dev/terraform-controller/api/types" + terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1" "github.com/pkg/errors" v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -44,6 +46,7 @@ import ( "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" + "github.com/oam-dev/kubevela/apis/types" "github.com/oam-dev/kubevela/pkg/appfile" "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/applicationconfiguration" "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/applicationrollout" @@ -214,8 +217,9 @@ func (h *appHandler) statusAggregate(appFile *appfile.Appfile) ([]common.Applica var ( outputSecretName string err error + pCtx process.Context ) - pCtx := process.NewContext(h.app.Namespace, wl.Name, appFile.Name, appFile.RevisionName) + if wl.IsCloudResourceProducer() { outputSecretName, err = appfile.GetOutputSecretNames(wl) if err != nil { @@ -223,29 +227,49 @@ func (h *appHandler) statusAggregate(appFile *appfile.Appfile) ([]common.Applica } pCtx.InsertSecrets(outputSecretName, wl.RequiredSecrets) } - if err := wl.EvalContext(pCtx); err != nil { - return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, evaluate context error", appFile.Name, wl.Name) + + switch wl.CapabilityCategory { + case types.TerraformCategory: + pCtx = appfile.NewBasicContext(wl, appFile.Name, appFile.RevisionName, appFile.Namespace) + ctx := context.Background() + var configuration terraformapi.Configuration + if err := h.r.Client.Get(ctx, client.ObjectKey{Name: wl.Name, Namespace: h.app.Namespace}, &configuration); err != nil { + return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, check health error", appFile.Name, wl.Name) + } + if configuration.Status.State != terraformtypes.Available { + healthy = false + status.Healthy = false + } else { + status.Healthy = true + } + status.Message = configuration.Status.Message + default: + pCtx = process.NewContext(h.app.Namespace, wl.Name, appFile.Name, appFile.RevisionName) + if err := wl.EvalContext(pCtx); err != nil { + return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, evaluate context error", appFile.Name, wl.Name) + } + workloadHealth, err := wl.EvalHealth(pCtx, h.r, h.app.Namespace) + if err != nil { + return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, check health error", appFile.Name, wl.Name) + } + if !workloadHealth { + // TODO(wonderflow): we should add a custom way to let the template say why it's unhealthy, only a bool flag is not enough + status.Healthy = false + healthy = false + } + + status.Message, err = wl.EvalStatus(pCtx, h.r, h.app.Namespace) + if err != nil { + return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, evaluate workload status message error", appFile.Name, wl.Name) + } } + for _, tr := range wl.Traits { if err := tr.EvalContext(pCtx); err != nil { return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, evaluate context error", appFile.Name, wl.Name, tr.Name) } } - workloadHealth, err := wl.EvalHealth(pCtx, h.r, h.app.Namespace) - if err != nil { - return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, check health error", appFile.Name, wl.Name) - } - if !workloadHealth { - // TODO(wonderflow): we should add a custom way to let the template say why it's unhealthy, only a bool flag is not enough - status.Healthy = false - healthy = false - } - - status.Message, err = wl.EvalStatus(pCtx, h.r, h.app.Namespace) - if err != nil { - return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, evaluate workload status message error", appFile.Name, wl.Name) - } var traitStatusList []common.ApplicationTraitStatus for _, trait := range wl.Traits { var traitStatus = common.ApplicationTraitStatus{ diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/apply_test.go b/pkg/controller/core.oam.dev/v1alpha2/application/apply_test.go index a0e8b872f..0e95acd14 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/apply_test.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/apply_test.go @@ -22,21 +22,27 @@ import ( "strings" "time" + "github.com/crossplane/crossplane-runtime/pkg/logging" "github.com/ghodss/yaml" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - + terraformtypes "github.com/oam-dev/terraform-controller/api/types" + terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - - "github.com/crossplane/crossplane-runtime/pkg/logging" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/oam-dev/kubevela/apis/core.oam.dev/common" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2" "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" + velatypes "github.com/oam-dev/kubevela/apis/types" + "github.com/oam-dev/kubevela/pkg/appfile" "github.com/oam-dev/kubevela/pkg/controller/utils" + "github.com/oam-dev/kubevela/pkg/oam/util" + "github.com/oam-dev/kubevela/pkg/utils/apply" ) const workloadDefinition = ` @@ -235,3 +241,103 @@ var _ = Describe("Test Application apply", func() { Expect(strings.Compare(applabel, app.Name) == 0).Should(BeTrue()) }) }) + +var _ = Describe("Test applyHelmModuleResources", func() { + var handler appHandler + var app *v1beta1.Application + var ctx context.Context + + BeforeEach(func() { + ctx = context.TODO() + app = &v1beta1.Application{} + handler = appHandler{ + r: reconciler, + app: app, + logger: reconciler.Log.WithValues("application", "unit-test"), + } + handler.r.applicator = apply.NewAPIApplicator(reconciler.Client) + }) + + It("helm component is complete", func() { + release := map[string]interface{}{"chart": map[string]interface{}{"spec": map[string]interface{}{"chart": "abc", "version": "v1"}}} + repo := map[string]interface{}{"url": "http://abc.com"} + comp := &v1alpha2.Component{Spec: v1alpha2.ComponentSpec{Helm: &common.Helm{ + Release: util.Object2RawExtension(release), + Repository: util.Object2RawExtension(repo), + }}} + err := handler.applyHelmModuleResources(ctx, comp, nil) + Expect(err).Should(HaveOccurred()) + + }) + + It("helm repo format is invalid", func() { + comp := &v1alpha2.Component{Spec: v1alpha2.ComponentSpec{Helm: &common.Helm{ + Repository: runtime.RawExtension{Raw: []byte("abc")}}}} + err := handler.applyHelmModuleResources(ctx, comp, nil) + Expect(err).Should(HaveOccurred()) + }) + + It("helm release format is invalid", func() { + repo := map[string]interface{}{"url": "http://abc.com"} + comp := &v1alpha2.Component{Spec: v1alpha2.ComponentSpec{Helm: &common.Helm{ + Release: runtime.RawExtension{Raw: []byte("abc")}, + Repository: util.Object2RawExtension(repo), + }}} + err := handler.applyHelmModuleResources(ctx, comp, nil) + Expect(err).Should(HaveOccurred()) + }) +}) + +var _ = Describe("Test statusAggregate", func() { + It("the component is Terraform type", func() { + var ( + ctx = context.TODO() + componentName = "sample-oss" + ns = "default" + h = &appHandler{r: reconciler, app: &v1beta1.Application{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{Namespace: ns}, + }} + appFile = &appfile.Appfile{ + Workloads: []*appfile.Workload{ + { + Name: componentName, + FullTemplate: &appfile.Template{Reference: common.WorkloadGVK{APIVersion: "v1", Kind: "A1"}}, + CapabilityCategory: velatypes.TerraformCategory, + }, + }, + } + ) + + By("aggregate status") + statuses, healthy, err := h.statusAggregate(appFile) + Expect(statuses).Should(BeNil()) + Expect(healthy).Should(Equal(false)) + Expect(err).Should(HaveOccurred()) + + By("create Terraform configuration") + configuration := terraformapi.Configuration{ + TypeMeta: metav1.TypeMeta{APIVersion: "terraform.core.oam.dev/v1beta1", Kind: "Configuration"}, + ObjectMeta: metav1.ObjectMeta{Name: componentName, Namespace: ns}, + } + k8sClient.Create(ctx, &configuration) + + By("aggregate status again") + statuses, healthy, err = h.statusAggregate(appFile) + Expect(len(statuses)).Should(Equal(1)) + Expect(healthy).Should(Equal(false)) + Expect(err).Should(BeNil()) + + By("set status for Terraform configuration") + var gotConfiguration terraformapi.Configuration + k8sClient.Get(ctx, client.ObjectKey{Namespace: ns, Name: componentName}, &gotConfiguration) + gotConfiguration.Status.State = terraformtypes.Available + k8sClient.Status().Update(ctx, &gotConfiguration) + + By("aggregate status one more time") + statuses, healthy, err = h.statusAggregate(appFile) + Expect(len(statuses)).Should(Equal(1)) + Expect(healthy).Should(Equal(true)) + Expect(err).Should(BeNil()) + }) +}) diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/suite_test.go b/pkg/controller/core.oam.dev/v1alpha2/application/suite_test.go index abe0d60b3..98c682a23 100644 --- a/pkg/controller/core.oam.dev/v1alpha2/application/suite_test.go +++ b/pkg/controller/core.oam.dev/v1alpha2/application/suite_test.go @@ -24,17 +24,15 @@ import ( "testing" "time" - "github.com/pkg/errors" - - "k8s.io/apimachinery/pkg/api/meta" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "github.com/crossplane/crossplane-runtime/pkg/event" "github.com/crossplane/crossplane-runtime/pkg/logging" "github.com/go-logr/logr" + terraformv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" @@ -101,7 +99,7 @@ var _ = BeforeSuite(func(done Done) { logf.Log.Info("start application suit test", "yaml_path", yamlPath) testEnv = &envtest.Environment{ UseExistingCluster: pointer.BoolPtr(false), - CRDDirectoryPaths: []string{yamlPath}, + CRDDirectoryPaths: []string{yamlPath, "./testdata/crds/terraform.core.oam.dev_configurations.yaml"}, } var err error @@ -120,6 +118,9 @@ var _ = BeforeSuite(func(done Done) { err = scheme.AddToScheme(testScheme) Expect(err).NotTo(HaveOccurred()) + + terraformv1beta1.AddToScheme(testScheme) + // +kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: testScheme}) Expect(err).ToNot(HaveOccurred()) diff --git a/pkg/controller/core.oam.dev/v1alpha2/application/testdata/crds/terraform.core.oam.dev_configurations.yaml b/pkg/controller/core.oam.dev/v1alpha2/application/testdata/crds/terraform.core.oam.dev_configurations.yaml new file mode 100644 index 000000000..42019b319 --- /dev/null +++ b/pkg/controller/core.oam.dev/v1alpha2/application/testdata/crds/terraform.core.oam.dev_configurations.yaml @@ -0,0 +1,117 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.2.5 + creationTimestamp: null + name: configurations.terraform.core.oam.dev +spec: + additionalPrinterColumns: + - JSONPath: .status.state + name: STATE + type: string + - JSONPath: .metadata.creationTimestamp + name: AGE + type: date + group: terraform.core.oam.dev + names: + kind: Configuration + listKind: ConfigurationList + plural: configurations + singular: configuration + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: Configuration is the Schema for the configurations API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ConfigurationSpec defines the desired state of Configuration + properties: + JSON: + description: JSON is the Terraform JSON syntax configuration + type: string + backend: + description: Backend stores the state in a Kubernetes secret with locking + done using a Lease resource. TODO(zzxwill) If a backend exists in + HCL/JSON, this can be optional. Currently, if Backend is not set by + users, it still will set by the controller, ignoring the settings + in HCL/JSON backend + properties: + inClusterConfig: + description: InClusterConfig Used to authenticate to the cluster + from inside a pod. Only `true` is allowed + type: boolean + secretSuffix: + description: 'SecretSuffix used when creating secrets. Secrets will + be named in the format: tfstate-{workspace}-{secretSuffix}' + type: string + type: object + hcl: + description: HCL is the Terraform HCL type configuration + type: string + variable: + type: object + x-kubernetes-preserve-unknown-fields: true + writeConnectionSecretToRef: + description: WriteConnectionSecretToReference specifies the namespace + and name of a Secret to which any connection details for this managed + resource should be written. Connection details frequently include + the endpoint, username, and password required to connect to the managed + resource. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + type: object + type: object + status: + description: ConfigurationStatus defines the observed state of Configuration + properties: + message: + type: string + outputs: + additionalProperties: + properties: + type: + type: string + value: + type: string + type: object + type: object + state: + description: A ResourceState represents the status of a resource + type: string + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/pkg/utils/common/common.go b/pkg/utils/common/common.go index 1ac4aa19c..f6c6cf4f9 100644 --- a/pkg/utils/common/common.go +++ b/pkg/utils/common/common.go @@ -33,6 +33,7 @@ import ( "cuelang.org/go/encoding/openapi" "github.com/AlecAivazis/survey/v2" "github.com/ghodss/yaml" + terraformv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1" kruise "github.com/openkruise/kruise-api/apps/v1alpha1" certmanager "github.com/wonderflow/cert-manager-api/pkg/apis/certmanager/v1" istioclientv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" @@ -59,6 +60,7 @@ func init() { _ = istioclientv1beta1.AddToScheme(Scheme) _ = certmanager.AddToScheme(Scheme) _ = kruise.AddToScheme(Scheme) + _ = terraformv1beta1.AddToScheme(Scheme) // +kubebuilder:scaffold:scheme }