diff --git a/components/dashboard/servicemesh_setup.go b/components/dashboard/servicemesh_setup.go index 81dcbb279fc..83365dcd14e 100644 --- a/components/dashboard/servicemesh_setup.go +++ b/components/dashboard/servicemesh_setup.go @@ -2,7 +2,6 @@ package dashboard import ( "path" - "path/filepath" operatorv1 "github.com/openshift/api/operator/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -43,15 +42,10 @@ func (d *Dashboard) configureServiceMesh(cli client.Client, owner metav1.Object, func (d *Dashboard) defineServiceMeshFeatures(dscispec *dsci.DSCInitializationSpec) feature.DefinedFeatures { return func(s *feature.FeaturesInitializer) error { - var rootDir = filepath.Join(feature.BaseOutputDir, dscispec.ApplicationsNamespace) - if err := feature.CopyEmbeddedFiles("templates", rootDir); err != nil { - return err - } - createMeshResources, err := feature.CreateFeature("dashboard-create-service-mesh-routing-resources"). For(dscispec). Manifests( - path.Join(rootDir, feature.ControlPlaneDir, "components", d.GetComponentName()), + path.Join(feature.ControlPlaneDir, "components", d.GetComponentName()), ). WithResources(servicemesh.EnabledInDashboard). WithData( diff --git a/components/kserve/serverless_setup.go b/components/kserve/serverless_setup.go index a86f88ad373..83d2b6cf877 100644 --- a/components/kserve/serverless_setup.go +++ b/components/kserve/serverless_setup.go @@ -2,7 +2,6 @@ package kserve import ( "path" - "path/filepath" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/feature" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/feature/serverless" @@ -16,15 +15,10 @@ const ( ) func (k *Kserve) configureServerlessFeatures(s *feature.FeaturesInitializer) error { - var rootDir = filepath.Join(feature.BaseOutputDir, s.DSCInitializationSpec.ApplicationsNamespace) - if err := feature.CopyEmbeddedFiles(templatesDir, rootDir); err != nil { - return err - } - servingDeployment, err := feature.CreateFeature("serverless-serving-deployment"). For(s.DSCInitializationSpec). Manifests( - path.Join(rootDir, templatesDir, "serving-install"), + path.Join(templatesDir, "serving-install"), ). WithData(PopulateComponentSettings(k)). PreConditions( @@ -55,7 +49,7 @@ func (k *Kserve) configureServerlessFeatures(s *feature.FeaturesInitializer) err ). WithResources(serverless.ServingCertificateResource). Manifests( - path.Join(rootDir, templatesDir, "serving-istio-gateways"), + path.Join(templatesDir, "serving-istio-gateways"), ). Load() if err != nil { diff --git a/controllers/dscinitialization/servicemesh_setup.go b/controllers/dscinitialization/servicemesh_setup.go index 1b4b140cb1c..800d5e27875 100644 --- a/controllers/dscinitialization/servicemesh_setup.go +++ b/controllers/dscinitialization/servicemesh_setup.go @@ -2,7 +2,6 @@ package dscinitialization import ( "path" - "path/filepath" operatorv1 "github.com/openshift/api/operator/v1" corev1 "k8s.io/api/core/v1" @@ -14,17 +13,12 @@ import ( ) func defineServiceMeshFeatures(f *feature.FeaturesInitializer) error { - var rootDir = filepath.Join(feature.BaseOutputDir, f.DSCInitializationSpec.ApplicationsNamespace) - if err := feature.CopyEmbeddedFiles("templates", rootDir); err != nil { - return err - } - serviceMeshSpec := f.ServiceMesh smcpCreation, errSmcp := feature.CreateFeature("service-mesh-control-plane-creation"). For(f.DSCInitializationSpec). Manifests( - path.Join(rootDir, feature.ControlPlaneDir, "base", "control-plane.tmpl"), + path.Join(feature.ControlPlaneDir, "base", "control-plane.tmpl"), ). PreConditions( servicemesh.EnsureServiceMeshOperatorInstalled, @@ -43,7 +37,7 @@ func defineServiceMeshFeatures(f *feature.FeaturesInitializer) error { metricsCollection, errMetrics := feature.CreateFeature("service-mesh-monitoring"). For(f.DSCInitializationSpec). Manifests( - path.Join(rootDir, feature.MonitoringDir), + path.Join(feature.MonitoringDir), ). PreConditions( servicemesh.EnsureServiceMeshInstalled, @@ -59,9 +53,9 @@ func defineServiceMeshFeatures(f *feature.FeaturesInitializer) error { if oauth, err := feature.CreateFeature("service-mesh-control-plane-configure-oauth"). For(f.DSCInitializationSpec). Manifests( - path.Join(rootDir, feature.ControlPlaneDir, "base"), - path.Join(rootDir, feature.ControlPlaneDir, "oauth"), - path.Join(rootDir, feature.ControlPlaneDir, "filters"), + path.Join(feature.ControlPlaneDir, "base"), + path.Join(feature.ControlPlaneDir, "oauth"), + path.Join(feature.ControlPlaneDir, "filters"), ). WithResources( servicemesh.DefaultValues, @@ -96,8 +90,8 @@ func defineServiceMeshFeatures(f *feature.FeaturesInitializer) error { if serviceMesh, err := feature.CreateFeature("app-add-namespace-to-service-mesh"). For(f.DSCInitializationSpec). Manifests( - path.Join(rootDir, feature.ControlPlaneDir, "smm.tmpl"), - path.Join(rootDir, feature.ControlPlaneDir, "namespace.patch.tmpl"), + path.Join(feature.ControlPlaneDir, "smm.tmpl"), + path.Join(feature.ControlPlaneDir, "namespace.patch.tmpl"), ). WithData(servicemesh.ClusterDetails). Load(); err != nil { @@ -109,7 +103,7 @@ func defineServiceMeshFeatures(f *feature.FeaturesInitializer) error { if gatewayRoute, err := feature.CreateFeature("service-mesh-create-gateway-route"). For(f.DSCInitializationSpec). Manifests( - path.Join(rootDir, feature.ControlPlaneDir, "routing"), + path.Join(feature.ControlPlaneDir, "routing"), ). WithData(servicemesh.ClusterDetails). PostConditions( @@ -133,10 +127,10 @@ func defineServiceMeshFeatures(f *feature.FeaturesInitializer) error { if extAuthz, err := feature.CreateFeature("service-mesh-control-plane-setup-external-authorization"). For(f.DSCInitializationSpec). Manifests( - path.Join(rootDir, feature.AuthDir, "auth-smm.tmpl"), - path.Join(rootDir, feature.AuthDir, "base"), - path.Join(rootDir, feature.AuthDir, "rbac"), - path.Join(rootDir, feature.AuthDir, "mesh-authz-ext-provider.patch.tmpl"), + path.Join(feature.AuthDir, "auth-smm.tmpl"), + path.Join(feature.AuthDir, "base"), + path.Join(feature.AuthDir, "rbac"), + path.Join(feature.AuthDir, "mesh-authz-ext-provider.patch.tmpl"), ). WithData(servicemesh.ClusterDetails). PreConditions( @@ -153,7 +147,7 @@ func defineServiceMeshFeatures(f *feature.FeaturesInitializer) error { // // To make it part of Service Mesh we have to patch it with injection // enabled instead, otherwise it will not have proxy pod injected. - return f.ApplyManifest(path.Join(rootDir, feature.AuthDir, "deployment.injection.patch.tmpl")) + return f.ApplyManifest(path.Join(feature.AuthDir, "deployment.injection.patch.tmpl")) }, ). OnDelete(servicemesh.RemoveExtensionProvider). diff --git a/pkg/cluster/cluster_config.go b/pkg/cluster/cluster_config.go index e6e344931ea..acd56eb15b6 100644 --- a/pkg/cluster/cluster_config.go +++ b/pkg/cluster/cluster_config.go @@ -5,11 +5,9 @@ import ( "errors" "fmt" "io" - "k8s.io/client-go/rest" "net/http" "net/url" "os" - "sigs.k8s.io/controller-runtime/pkg/client/config" "strconv" "strings" @@ -17,6 +15,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client/config" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/gvr" ) @@ -46,7 +46,6 @@ func GetOAuthServerDetails() (*simplejson.Json, error) { } func request(method string, url string) ([]byte, error) { - restCfg, err := config.GetConfig() if err != nil { return nil, err diff --git a/pkg/feature/builder.go b/pkg/feature/builder.go index a3f8f1c4e0f..017241feb65 100644 --- a/pkg/feature/builder.go +++ b/pkg/feature/builder.go @@ -1,6 +1,8 @@ package feature import ( + "io/fs" + "github.com/pkg/errors" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/client-go/dynamic" @@ -79,7 +81,7 @@ func (fb *featureBuilder) Manifests(paths ...string) *featureBuilder { var manifests []manifest for _, path := range paths { - manifests, err = loadManifestsFrom(path) + manifests, err = loadManifestsFrom(f.fsys, path) if err != nil { return errors.WithStack(err) } @@ -156,6 +158,7 @@ func (fb *featureBuilder) Load() (*Feature, error) { feature := &Feature{ Name: fb.name, Enabled: true, + fsys: embeddedFiles, } for i := range fb.builders { @@ -185,3 +188,15 @@ func (fb *featureBuilder) Load() (*Feature, error) { return feature, nil } + +// ManifestSource sets the root file system (fs.FS) from which manifest paths are loaded +// If ManifestSource is not called in the builder chain, the default source will be the embeddedFiles. +func (fb *featureBuilder) ManifestSource(fsys fs.FS) *featureBuilder { + fb.builders = append(fb.builders, func(f *Feature) error { + f.fsys = fsys + + return nil + }) + + return fb +} diff --git a/pkg/feature/feature.go b/pkg/feature/feature.go index dcd95946116..ec6fd0a2ff9 100644 --- a/pkg/feature/feature.go +++ b/pkg/feature/feature.go @@ -2,6 +2,7 @@ package feature import ( "context" + "io/fs" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" @@ -31,7 +32,9 @@ type Feature struct { DynamicClient dynamic.Interface Client client.Client - manifests []manifest + fsys fs.FS + manifests []manifest + cleanups []Action resources []Action preconditions []Action @@ -67,7 +70,7 @@ func (f *Feature) Apply() error { return multiErr.ErrorOrNil() } - // create or update resources + // Create or update resources for _, resource := range f.resources { if err := resource(f); err != nil { return err @@ -75,27 +78,24 @@ func (f *Feature) Apply() error { } // Process and apply manifests - for _, m := range f.manifests { - if err := m.processTemplate(f.Spec); err != nil { + for i, m := range f.manifests { + if err := m.processTemplate(f.fsys, f.Spec); err != nil { return errors.WithStack(err) } - log.Info("converted template to manifest", "feature", f.Name, "path", m.targetPath()) + f.manifests[i] = m } if err := f.applyManifests(); err != nil { return err } + // Check all postconditions and collect errors for _, postcondition := range f.postconditions { multiErr = multierror.Append(multiErr, postcondition(f)) } - if multiErr.ErrorOrNil() != nil { - return multiErr.ErrorOrNil() - } - - return nil + return multiErr.ErrorOrNil() } func (f *Feature) Cleanup() error { @@ -159,34 +159,34 @@ func (f *Feature) addCleanup(cleanupFuncs ...Action) { func (f *Feature) ApplyManifest(filename string) error { m := loadManifestFrom(filename) - if err := m.processTemplate(f.Spec); err != nil { + if err := m.processTemplate(f.fsys, f.Spec); err != nil { return err } return f.apply(m) } -type apply func(filename string) error +type apply func(data string) error func (f *Feature) apply(m manifest) error { var applier apply targetPath := m.targetPath() if m.patch { - applier = func(filename string) error { + applier = func(data string) error { log.Info("patching using manifest", "feature", f.Name, "name", m.name, "path", targetPath) - return f.patchResourceFromFile(filename) + return f.patchResources(data) } } else { - applier = func(filename string) error { + applier = func(data string) error { log.Info("applying manifest", "feature", f.Name, "name", m.name, "path", targetPath) - return f.createResourceFromFile(filename) + return f.createResources(data) } } - if err := applier(targetPath); err != nil { + if err := applier(m.processedContent); err != nil { log.Error(err, "failed to create resource", "feature", f.Name, "name", m.name, "path", targetPath) return err diff --git a/pkg/feature/manifest.go b/pkg/feature/manifest.go index 71159b0684b..195766619d1 100644 --- a/pkg/feature/manifest.go +++ b/pkg/feature/manifest.go @@ -1,46 +1,58 @@ package feature import ( + "bytes" + "embed" "fmt" "html/template" - "os" + "io" + "io/fs" "path/filepath" "strings" - - "github.com/pkg/errors" ) +//go:embed templates +var embeddedFiles embed.FS + const ( BaseDir = "templates/servicemesh/" ControlPlaneDir = BaseDir + "control-plane" AuthDir = BaseDir + "authorino" MonitoringDir = BaseDir + "monitoring" - BaseOutputDir = "/tmp/opendatahub-manifests/" ) type manifest struct { name, - path string + path, + processedContent string template, - patch, - processed bool + patch bool } -func loadManifestsFrom(path string) ([]manifest, error) { +func loadManifestsFrom(fsys fs.FS, path string) ([]manifest, error) { var manifests []manifest - if err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { + + err := fs.WalkDir(fsys, path, func(path string, dirEntry fs.DirEntry, err error) error { if err != nil { return err } - if info.IsDir() { + + if dirEntry.IsDir() { return nil } + + _, err = fs.ReadFile(fsys, path) + if err != nil { + return err + } m := loadManifestFrom(path) manifests = append(manifests, m) return nil - }); err != nil { - return nil, errors.WithStack(err) + }) + + if err != nil { + return nil, err } return manifests, nil @@ -62,28 +74,36 @@ func (m *manifest) targetPath() string { return fmt.Sprintf("%s%s", m.path[:len(m.path)-len(filepath.Ext(m.path))], ".yaml") } -func (m *manifest) processTemplate(data interface{}) error { +func (m *manifest) processTemplate(fsys fs.FS, data interface{}) error { if !m.template { return nil } - path := m.targetPath() - f, err := os.Create(path) + templateFile, err := fsys.Open(m.path) if err != nil { - log.Error(err, "Failed to create file") - + log.Error(err, "Failed to open template file", "path", m.path) return err } + defer templateFile.Close() - tmpl := template.New(m.name).Funcs(template.FuncMap{"ReplaceChar": ReplaceChar}) + templateContent, err := io.ReadAll(templateFile) + if err != nil { + log.Error(err, "Failed to read template file", "path", m.path) + return err + } - tmpl, err = tmpl.ParseFiles(m.path) + tmpl, err := template.New(m.name).Funcs(template.FuncMap{"ReplaceChar": ReplaceChar}).Parse(string(templateContent)) if err != nil { + log.Error(err, "Failed to template for file", "path", m.path) + return err + } + + var buffer bytes.Buffer + if err := tmpl.Execute(&buffer, data); err != nil { return err } - err = tmpl.Execute(f, data) - m.processed = err == nil + m.processedContent = buffer.String() - return err + return nil } diff --git a/pkg/feature/raw_resources.go b/pkg/feature/raw_resources.go index bf3abea8e8b..a0844f9d82f 100644 --- a/pkg/feature/raw_resources.go +++ b/pkg/feature/raw_resources.go @@ -15,8 +15,6 @@ package feature import ( "context" - "fmt" - "os" "regexp" "strings" @@ -33,13 +31,9 @@ const ( YamlSeparator = "(?m)^---[ \t]*$" ) -func (f *Feature) createResourceFromFile(filename string) error { - data, err := os.ReadFile(filename) - if err != nil { - return errors.WithStack(err) - } +func (f *Feature) createResources(resources string) error { splitter := regexp.MustCompile(YamlSeparator) - objectStrings := splitter.Split(string(data), -1) + objectStrings := splitter.Split(resources, -1) for _, str := range objectStrings { if strings.TrimSpace(str) == "" { continue @@ -79,13 +73,9 @@ func (f *Feature) createResourceFromFile(filename string) error { return nil } -func (f *Feature) patchResourceFromFile(filename string) error { - data, err := os.ReadFile(filename) - if err != nil { - return errors.WithStack(err) - } +func (f *Feature) patchResources(resources string) error { splitter := regexp.MustCompile(YamlSeparator) - objectStrings := splitter.Split(string(data), -1) + objectStrings := splitter.Split(resources, -1) for _, str := range objectStrings { if strings.TrimSpace(str) == "" { continue @@ -93,7 +83,6 @@ func (f *Feature) patchResourceFromFile(filename string) error { u := &unstructured.Unstructured{} if err := yaml.Unmarshal([]byte(str), u); err != nil { log.Error(err, "error unmarshalling yaml") - return errors.WithStack(err) } @@ -105,25 +94,16 @@ func (f *Feature) patchResourceFromFile(filename string) error { Resource: strings.ToLower(u.GroupVersionKind().Kind) + "s", } - // Convert the patch from YAML to JSON - patchAsJSON, err := yaml.YAMLToJSON(data) + // Convert the individual resource patch from YAML to JSON + patchAsJSON, err := yaml.YAMLToJSON([]byte(str)) if err != nil { log.Error(err, "error converting yaml to json") - return errors.WithStack(err) } _, err = f.DynamicClient.Resource(gvr). Namespace(u.GetNamespace()). Patch(context.TODO(), u.GetName(), k8stypes.MergePatchType, patchAsJSON, metav1.PatchOptions{}) - if err != nil { - log.Error(err, "error patching resource", - "gvr", fmt.Sprintf("%+v\n", gvr), - "patch", fmt.Sprintf("%+v\n", u), - "json", fmt.Sprintf("%+v\n", patchAsJSON)) - - return errors.WithStack(err) - } if err != nil { return errors.WithStack(err) diff --git a/pkg/feature/servicemesh/loaders.go b/pkg/feature/servicemesh/loaders.go index 02f3b650b37..148da8d669d 100644 --- a/pkg/feature/servicemesh/loaders.go +++ b/pkg/feature/servicemesh/loaders.go @@ -1,9 +1,10 @@ package servicemesh import ( - "github.com/pkg/errors" "strings" + "github.com/pkg/errors" + "github.com/opendatahub-io/opendatahub-operator/v2/controllers/secretgenerator" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster" "github.com/opendatahub-io/opendatahub-operator/v2/pkg/feature" diff --git a/pkg/feature/template_loader.go b/pkg/feature/template_loader.go deleted file mode 100644 index 9d2fd53cf1e..00000000000 --- a/pkg/feature/template_loader.go +++ /dev/null @@ -1,39 +0,0 @@ -package feature - -import ( - "embed" - "io/fs" - "os" - "path/filepath" -) - -//go:embed templates -var embeddedFiles embed.FS - -// CopyEmbeddedFiles ensures that files embedded using go:embed are populated -// to dest directory. In order to process the templates, we need to create a tmp directory -// to store the files. This is because embedded files are read only. -func CopyEmbeddedFiles(src, dest string) error { - return fs.WalkDir(embeddedFiles, src, func(path string, dir fs.DirEntry, err error) error { - if err != nil { - return err - } - - destPath := filepath.Join(dest, path) - if dir.IsDir() { - if err := os.MkdirAll(destPath, 0755); err != nil { - return err - } - } else { - data, err := fs.ReadFile(embeddedFiles, path) - if err != nil { - return err - } - if err := os.WriteFile(destPath, data, 0644); err != nil { - return err - } - } - - return nil - }) -} diff --git a/tests/integration/servicemesh/service_mesh_setup_features_int_test.go b/tests/integration/servicemesh/service_mesh_setup_features_int_test.go index 88d6deb72e5..40c23507cab 100644 --- a/tests/integration/servicemesh/service_mesh_setup_features_int_test.go +++ b/tests/integration/servicemesh/service_mesh_setup_features_int_test.go @@ -2,6 +2,7 @@ package servicemesh_test import ( "context" + "embed" "io" "os" "path" @@ -27,9 +28,14 @@ import ( . "github.com/onsi/gomega" ) +//go:embed test-templates +var testEmbeddedFiles embed.FS + const ( - timeout = 5 * time.Second - interval = 250 * time.Millisecond + timeout = 5 * time.Second + interval = 250 * time.Millisecond + authorinoName = "authorino" + authorinoNamespace = "test-provider" ) var _ = Describe("preconditions", func() { @@ -301,7 +307,7 @@ var _ = Describe("Cleanup operations", func() { controlPlaneWithSecretVolumes, err := feature.CreateFeature("control-plane-with-secret-volumes"). For(dsciSpec). - Manifests(fromTestTmpDir(path.Join(feature.ControlPlaneDir, "base/control-plane-ingress.patch.tmpl"))). + Manifests(path.Join(feature.ControlPlaneDir, "base/control-plane-ingress.patch.tmpl")). UsingConfig(envTest.Config). Load() @@ -328,14 +334,14 @@ var _ = Describe("Cleanup operations", func() { Expect(envTestClient.Create(context.Background(), ns)).To(Succeed()) defer objectCleaner.DeleteAll(ns) - serviceMeshSpec.Auth.Namespace = "test-provider" - serviceMeshSpec.Auth.Authorino.Name = "authorino" + serviceMeshSpec.Auth.Namespace = authorinoNamespace + serviceMeshSpec.Auth.Authorino.Name = authorinoName createServiceMeshControlPlane(name, namespace) controlPlaneWithExtAuthzProvider, err := feature.CreateFeature("control-plane-with-external-authz-provider"). For(dsciSpec). - Manifests(fromTestTmpDir(path.Join(feature.AuthDir, "mesh-authz-ext-provider.patch.tmpl"))). + Manifests(path.Join(feature.AuthDir, "mesh-authz-ext-provider.patch.tmpl")). UsingConfig(envTest.Config). Load() @@ -374,6 +380,110 @@ var _ = Describe("Cleanup operations", func() { }) +var _ = Describe("Alternate Manifest source", func() { + + Context("using a non-default manifest source", func() { + + var ( + objectCleaner *envtestutil.Cleaner + dsciSpec *dscv1.DSCInitializationSpec + serviceMeshSpec *infrav1.ServiceMeshSpec + namespace = "test" + name = "minimal" + ) + + BeforeEach(func() { + objectCleaner = envtestutil.CreateCleaner(envTestClient, envTest.Config, timeout, interval) + + dsciSpec = newDSCInitializationSpec(namespace) + + serviceMeshSpec = &dsciSpec.ServiceMesh + + serviceMeshSpec.ControlPlane.Name = name + serviceMeshSpec.ControlPlane.Namespace = namespace + }) + + It("should be able to use manifests embedded from different location", func() { + // given + ns := createNamespace(namespace) + Expect(envTestClient.Create(context.Background(), ns)).To(Succeed()) + defer objectCleaner.DeleteAll(ns) + + serviceMeshSpec.Auth.Namespace = authorinoNamespace + serviceMeshSpec.Auth.Authorino.Name = authorinoName + + createServiceMeshControlPlane(name, namespace) + + controlPlaneWithExtAuthzProvider, err := feature.CreateFeature("external-manifests-control-plane-with-external-authz-provider"). + For(dsciSpec). + ManifestSource(testEmbeddedFiles). + Manifests(path.Join("test-templates", "authorino", "mesh-authz-ext-provider.patch.tmpl")). + UsingConfig(envTest.Config). + Load() + + Expect(err).ToNot(HaveOccurred()) + + // when + By("verifying extension provider has been added after applying feature", func() { + Expect(controlPlaneWithExtAuthzProvider.Apply()).To(Succeed()) + serviceMeshControlPlane, err := getServiceMeshControlPlane(envTest.Config, namespace, name) + Expect(err).ToNot(HaveOccurred()) + + extensionProviders, found, err := unstructured.NestedSlice(serviceMeshControlPlane.Object, "spec", "techPreview", "meshConfig", "extensionProviders") + Expect(err).ToNot(HaveOccurred()) + Expect(found).To(BeTrue()) + + extensionProvider := extensionProviders[0].(map[string]interface{}) + Expect(extensionProvider["name"]).To(Equal("test-odh-different-auth-provider")) + Expect(extensionProvider["envoyExtAuthzGrpc"].(map[string]interface{})["service"]).To(Equal("authorino-authorino-authorization.test-provider.svc.cluster.local")) + }) + + }) + + It("should be able to use an actual file system", func() { + // given + tempDir := GinkgoT().TempDir() + + ns := createNamespace(namespace) + Expect(envTestClient.Create(context.Background(), ns)).To(Succeed()) + defer objectCleaner.DeleteAll(ns) + + serviceMeshSpec.Auth.Namespace = authorinoNamespace + serviceMeshSpec.Auth.Authorino.Name = authorinoName + + patchTemplate := path.Join(feature.AuthDir, "mesh-authz-ext-provider.patch.tmpl") + copyEmbeddedTemplatesTo(tempDir, patchTemplate) + + createServiceMeshControlPlane(name, namespace) + + controlPlaneWithExtAuthzProvider, err := feature.CreateFeature("external-manifests-control-plane-with-external-authz-provider"). + For(dsciSpec). + ManifestSource(os.DirFS(tempDir)). + Manifests(patchTemplate). // must be relative to root DirFS defined above + UsingConfig(envTest.Config). + Load() + + Expect(err).ToNot(HaveOccurred()) + + // when + By("verifying extension provider has been added after applying feature", func() { + Expect(controlPlaneWithExtAuthzProvider.Apply()).To(Succeed()) + serviceMeshControlPlane, err := getServiceMeshControlPlane(envTest.Config, namespace, name) + Expect(err).ToNot(HaveOccurred()) + + extensionProviders, found, err := unstructured.NestedSlice(serviceMeshControlPlane.Object, "spec", "techPreview", "meshConfig", "extensionProviders") + Expect(err).ToNot(HaveOccurred()) + Expect(found).To(BeTrue()) + + extensionProvider := extensionProviders[0].(map[string]interface{}) + Expect(extensionProvider["name"]).To(Equal("test-odh-auth-provider")) + Expect(extensionProvider["envoyExtAuthzGrpc"].(map[string]interface{})["service"]).To(Equal("authorino-authorino-authorization.test-provider.svc.cluster.local")) + }) + + }) + }) +}) + func createServiceMeshControlPlane(name, namespace string) { serviceMeshControlPlane := &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -492,22 +602,17 @@ func getNamespace(namespace string) (*v1.Namespace, error) { return ns, err } -func fromTestTmpDir(fileName string) string { +func copyEmbeddedTemplatesTo(tmpDir string, templatePaths ...string) { root, err := envtestutil.FindProjectRoot() Expect(err).ToNot(HaveOccurred()) - tmpDir := filepath.Join(os.TempDir(), envtestutil.RandomUUIDName(16)) - if err := os.Mkdir(tmpDir, os.ModePerm); err != nil { - Fail(err.Error()) - } - - src := path.Join(root, "pkg", "feature", fileName) - dest := path.Join(tmpDir, fileName) - if err := copyFile(src, dest); err != nil { - Fail(err.Error()) + for _, templatePath := range templatePaths { + src := path.Join(root, "pkg", "feature", templatePath) + dest := path.Join(tmpDir, templatePath) + if err := copyFile(src, dest); err != nil { + Fail(err.Error()) + } } - - return dest } func copyFile(src, dst string) error { diff --git a/tests/integration/servicemesh/test-templates/authorino/mesh-authz-ext-provider.patch.tmpl b/tests/integration/servicemesh/test-templates/authorino/mesh-authz-ext-provider.patch.tmpl new file mode 100644 index 00000000000..263676cb6a0 --- /dev/null +++ b/tests/integration/servicemesh/test-templates/authorino/mesh-authz-ext-provider.patch.tmpl @@ -0,0 +1,13 @@ +apiVersion: maistra.io/v2 +kind: ServiceMeshControlPlane +metadata: + name: {{ .ControlPlane.Name }} + namespace: {{ .ControlPlane.Namespace }} +spec: + techPreview: + meshConfig: + extensionProviders: + - name: {{ .AppNamespace }}-odh-different-auth-provider + envoyExtAuthzGrpc: + service: {{ .Auth.Authorino.Name }}-authorino-authorization.{{ .Auth.Namespace }}.svc.cluster.local + port: 50051