From e24df934a6fff86d64cfb4178c019e7f29ce62d7 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Fri, 11 Apr 2025 11:32:30 +0300 Subject: [PATCH 01/35] add plugin deps Signed-off-by: gazarenkov --- Makefile | 2 +- api/v1alpha1/zz_generated.deepcopy.go | 2 +- api/v1alpha2/zz_generated.deepcopy.go | 2 +- api/v1alpha3/zz_generated.deepcopy.go | 2 +- .../rhdh/default-config/dynamic-plugins.yaml | 90 ++++++++++-- .../orchestrator/samples/greetings.yaml | 130 +++++++++++++++++ internal/controller/backstage_controller.go | 8 +- internal/controller/plugin-deps.go | 61 ++++++++ pkg/utils/plugin_deps.go | 134 ++++++++++++++++++ pkg/utils/plugin_deps_test.go | 76 ++++++++++ pkg/utils/yaml.go | 83 +++++++++++ 11 files changed, 570 insertions(+), 20 deletions(-) create mode 100644 config/profile/rhdh/default-config/plugin-deps/orchestrator/samples/greetings.yaml create mode 100644 internal/controller/plugin-deps.go create mode 100644 pkg/utils/plugin_deps.go create mode 100644 pkg/utils/plugin_deps_test.go create mode 100644 pkg/utils/yaml.go diff --git a/Makefile b/Makefile index 11bda04b7..8a80e46ea 100644 --- a/Makefile +++ b/Makefile @@ -220,7 +220,7 @@ build: manifests generate fmt vet ## Build manager binary. run: manifests generate fmt vet $(LOCALBIN) ## Run a controller from your host. mkdir -p $(LOCALBIN)/default-config/ rm -fr $(LOCALBIN)/default-config/* - cp config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config/ + cp -r config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config/ go run -C $(LOCALBIN) ../cmd/main.go # by default images expire from quay registry after 14 days diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 9b7acdd38..ca4d7a346 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha1 import ( - "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index c3ecd6b25..1357c08bd 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha2 import ( - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index b315b789e..6613a8896 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha3 import ( - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/config/profile/rhdh/default-config/dynamic-plugins.yaml b/config/profile/rhdh/default-config/dynamic-plugins.yaml index 5c0c99a1a..db4737c8f 100644 --- a/config/profile/rhdh/default-config/dynamic-plugins.yaml +++ b/config/profile/rhdh/default-config/dynamic-plugins.yaml @@ -1,21 +1,81 @@ +#apiVersion: v1 +#kind: ConfigMap +#metadata: +# name: default-dynamic-plugins # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name +#data: +# "dynamic-plugins.yaml": | +# ########################################################################################################### +# # /!\ WARNING +# # +# # This is the default dynamic plugins configuration file created and managed by the Operator for your CR. +# # Do NOT edit this manually in the Cluster, as your changes will be overridden by the Operator upon the +# # next reconciliation. +# # If you want to customize the dynamic plugins, you should create your own dynamic-plugins ConfigMap +# # and reference it in your CR. +# # See https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.4/html/installing_and_viewing_plugins_in_red_hat_developer_hub/rhdh-installing-rhdh-plugins_title-plugins-rhdh-about#proc-config-dynamic-plugins-rhdh-operator_rhdh-installing-rhdh-plugins +# # for more details or https://github.com/redhat-developer/rhdh-operator/blob/main/examples/rhdh-cr.yaml +# # for an example. +# ########################################################################################################### +# includes: +# - dynamic-plugins.default.yaml +# plugins: [] +#--- apiVersion: v1 kind: ConfigMap metadata: - name: default-dynamic-plugins # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name + name: default-dynamic-plugins data: - "dynamic-plugins.yaml": | - ########################################################################################################### - # /!\ WARNING - # - # This is the default dynamic plugins configuration file created and managed by the Operator for your CR. - # Do NOT edit this manually in the Cluster, as your changes will be overridden by the Operator upon the - # next reconciliation. - # If you want to customize the dynamic plugins, you should create your own dynamic-plugins ConfigMap - # and reference it in your CR. - # See https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.4/html/installing_and_viewing_plugins_in_red_hat_developer_hub/rhdh-installing-rhdh-plugins_title-plugins-rhdh-about#proc-config-dynamic-plugins-rhdh-operator_rhdh-installing-rhdh-plugins - # for more details or https://github.com/redhat-developer/rhdh-operator/blob/main/examples/rhdh-cr.yaml - # for an example. - ########################################################################################################### + dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml - plugins: [] \ No newline at end of file + plugins: + - disabled: false + integrity: sha512-2aOHDLFrGMAtyHFiyGZwVBZ9Op+TmKYUwfZxwoaGJ1s6JSy/0qgqineEEE0K3dn/f17XBUj+H1dwa5Al598Ugw== + package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-backend-dynamic-1.4.0.tgz + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service + - disabled: false + integrity: sha512-2yasbfBZ3iKntArIfK+hk9tvv4b/dy9+WKXOcWIotqkI1gv+Nhvy+m55KAUWi2vmfM0rj3EoG6YP+3Zajn1KyA== + package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-1.4.0.tgz + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + module: OrchestratorPlugin + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + module: OrchestratorPlugin + path: /orchestrator +# - disabled: true +# package: ./dynamic-plugins/dist/backstage-plugin-notifications +# pluginConfig: +# dynamicPlugins: +# frontend: +# backstage.plugin-notifications: +# dynamicRoutes: +# - importName: NotificationsPage +# menuItem: +# config: +# props: +# titleCounterEnabled: true +# webNotificationsEnabled: false +# importName: NotificationsSidebarItem +# path: /notifications +# - disabled: true +# package: ./dynamic-plugins/dist/backstage-plugin-signals +# pluginConfig: +# dynamicPlugins: +# frontend: +# backstage.plugin-signals: {} +# - disabled: true +# package: ./dynamic-plugins/dist/backstage-plugin-notifications-backend-dynamic +# - disabled: true +# package: ./dynamic-plugins/dist/backstage-plugin-signals-backend-dynamic \ No newline at end of file diff --git a/config/profile/rhdh/default-config/plugin-deps/orchestrator/samples/greetings.yaml b/config/profile/rhdh/default-config/plugin-deps/orchestrator/samples/greetings.yaml new file mode 100644 index 000000000..e4955b6c9 --- /dev/null +++ b/config/profile/rhdh/default-config/plugin-deps/orchestrator/samples/greetings.yaml @@ -0,0 +1,130 @@ +apiVersion: v1 +data: + greeting.sw.input-schema.json: | + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "language": { + "title": "Language", + "description": "Language to greet", + "type": "string", + "enum": ["English", "Spanish"], + "default": "English" + } + } + } + workflow-output-schema.json: |- + { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "WorkflowResult", + "description": "Schema of workflow output", + "type": "object", + "properties": { + "result": { + "$ref": "../shared/schemas/workflow-result-schema.json", + "type": "object" + } + } + } +kind: ConfigMap +metadata: + name: 01-greeting-resources-schemas +--- +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlow +metadata: + annotations: + sonataflow.org/description: YAML based greeting workflow + sonataflow.org/expressionLang: jq + sonataflow.org/profile: gitops + sonataflow.org/version: "1.0" + labels: + app: greeting + sonataflow.org/workflow-app: greeting + name: greeting +spec: + flow: + annotations: + - workflow-type/infrastructure + dataInputSchema: + failOnValidationErrors: true + schema: schemas/greeting.sw.input-schema.json + functions: + - name: greetFunction + operation: sysout + type: custom + - name: successResult + operation: '{ "result": { "message": "Greeting workflow completed successfully", "outputs":[ { "key":"Selected language", "value": .language }, { "key":"Greeting message", "value": .greeting } ] } }' + type: expression + start: + stateName: ChooseOnLanguage + states: + - dataConditions: + - condition: .language == "English" + transition: + nextState: GreetInEnglish + - condition: .language == "Spanish" + transition: + nextState: GreetInSpanish + defaultCondition: + transition: + nextState: GreetInEnglish + name: ChooseOnLanguage + type: switch + - data: + greeting: Hello from YAML Workflow + name: GreetInEnglish + transition: + nextState: GreetPerson + type: inject + - data: + greeting: Saludos desde YAML Workflow + name: GreetInSpanish + transition: + nextState: GreetPerson + type: inject + - actionMode: sequential + actions: + - actionDataFilter: + useResults: true + functionRef: + arguments: + message: .greeting + invoke: sync + refName: greetFunction + name: greetAction + - actionDataFilter: + useResults: true + functionRef: + invoke: sync + refName: successResult + name: setOutput + end: + terminate: true + name: GreetPerson + type: operation + podTemplate: + container: + resources: {} + image: quay.io/orchestrator/serverless-workflow-greeting:96d772297d3ee964ab9775d7aae7dbbd4f070dd7 + resources: + configMaps: + - configMap: + name: 01-greeting-resources-schemas + workflowPath: schemas + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-bs1 # hardcoded backstage-psql-secret-{{cr-name}} + userKey: POSTGRES_USER #hardcoded + passwordKey: POSTGRES_PASSWORD #hardcoded + serviceRef: + name: backstage-psql-bs1 # hardcoded backstage-psql-{{cr-name}} + namespace: backstage # hardcoded {{cr-namespace}} + databaseName: backstage_plugin_orchestrator #hardcoded + port: 5432 + databaseSchema: greeting +status: + address: {} + diff --git a/internal/controller/backstage_controller.go b/internal/controller/backstage_controller.go index 5d3aec342..b647f99f0 100644 --- a/internal/controller/backstage_controller.go +++ b/internal/controller/backstage_controller.go @@ -105,6 +105,12 @@ func (r *BackstageReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, errorAndStatus(&backstage, "failed to initialize backstage model", err) } + // Apply the plugin dependencies + if err := r.applyPluginDeps(ctx, backstage.Namespace); err != nil { + return ctrl.Result{}, errorAndStatus(&backstage, "failed to apply plugin dependencies", err) + } + + // Apply the runtime objects err = r.applyObjects(ctx, bsModel.RuntimeObjects) if err != nil { return ctrl.Result{}, errorAndStatus(&backstage, "failed to apply backstage objects", err) @@ -120,7 +126,7 @@ func (r *BackstageReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( func errorAndStatus(backstage *bs.Backstage, msg string, err error) error { setStatusCondition(backstage, bs.BackstageConditionTypeDeployed, metav1.ConditionFalse, bs.BackstageConditionReasonFailed, fmt.Sprintf("%s %s", msg, err)) - return fmt.Errorf("%s %w", msg, err) + return fmt.Errorf("%s: %w", msg, err) } func (r *BackstageReconciler) applyObjects(ctx context.Context, objects []model.RuntimeObject) error { diff --git a/internal/controller/plugin-deps.go b/internal/controller/plugin-deps.go new file mode 100644 index 000000000..71433cf7c --- /dev/null +++ b/internal/controller/plugin-deps.go @@ -0,0 +1,61 @@ +package controller + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/redhat-developer/rhdh-operator/pkg/utils" + + "sigs.k8s.io/controller-runtime/pkg/log" + + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func (r *BackstageReconciler) applyPluginDeps(ctx context.Context, bsNamespace string) error { + + lg := log.FromContext(ctx) + dir := filepath.Join(os.Getenv("LOCALBIN"), "default-config", "plugin-deps") + + // Read all YAML files from the directory + //objects, err := utils.ReadYamlFilesFromDir(dir) + objects, err := utils.ReadPluginDeps(dir) + + if err != nil { + return fmt.Errorf("failed to read YAML files: %w", err) + } + + // Process the objects as needed + var errs []error + for _, obj := range objects { + // Apply the unstructured object + lg.V(1).Info("apply plugin dependency: ", "name", obj.GetName(), "kind", obj.GetKind(), "namespace", obj.GetNamespace()) + + // Set the namespace if not set + if obj.GetNamespace() == "" { + obj.SetNamespace(bsNamespace) + } + + if err := r.Patch(ctx, obj, client.Apply, &client.PatchOptions{FieldManager: BackstageFieldManager, Force: ptr.To(true)}); err != nil { + errs = append(errs, err) + } + } + + if len(errs) > 0 { + return combineErrors(errs) + } + + return nil +} + +func combineErrors(errs []error) error { + var sb strings.Builder + for _, err := range errs { + sb.WriteString(err.Error() + "\n") + } + return errors.New(sb.String()) +} diff --git a/pkg/utils/plugin_deps.go b/pkg/utils/plugin_deps.go new file mode 100644 index 000000000..8b5227871 --- /dev/null +++ b/pkg/utils/plugin_deps.go @@ -0,0 +1,134 @@ +package utils + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "strings" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func ReadPluginDeps(rootDir string) ([]*unstructured.Unstructured, error) { + + if !DirectoryExists(rootDir) { + return []*unstructured.Unstructured{}, nil + } + + var objects []*unstructured.Unstructured + + // Read allowed directories from the "enabled" file + enabledDirs, err := readEnabledDirs(filepath.Join(rootDir, "enabled")) + if err != nil { + return nil, err + } + + // Read the directory tree + files, err := processDepsTree(rootDir, enabledDirs) + + if err != nil { + return nil, err + } + + for _, file := range files { + if !isYamlFile(file) { + continue + } + objs, err := ReadYamlFile(file) + if err != nil { + return nil, fmt.Errorf("failed to read YAML file %s: %w", file, err) + } + objects = append(objects, objs...) + } + + return objects, nil +} + +func processDepsTree(root string, enabledDirs []string) ([]string, error) { + // Normalize and store allowed directories + enabledMap := make(map[string]bool) + for _, dir := range enabledDirs { + enabledMap[filepath.Clean(dir)] = true + } + files := []string{} + + // Traverse the directory tree + err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { + + if err != nil { + return err + } + + // Always allow traversal of directories + if d.IsDir() { + fmt.Println("Traversing directory:", path) + return nil + } + + // Only process files if the directory (or its parent) is allowed + if isEnabled(filepath.Dir(path), enabledMap) { + fmt.Println("Reading file:", path) + files = append(files, path) + } else { + fmt.Println("Skipping file:", path) + } + + return nil + }) + if err != nil { + return nil, err + } + return files, nil +} + +func isEnabled(path string, enabledMap map[string]bool) bool { + // Check if the path or any of its parent directories is in the allowed list + for { + if enabledMap[path] { + return true + } + parent := filepath.Dir(path) + if parent == path || parent == "." || parent == "/" { // Reached the root + break + } + path = parent + } + return false +} + +func readEnabledDirs(filePath string) ([]string, error) { + var enabledDirs []string + + root := filepath.Dir(filePath) + // Open the file + file, err := os.Open(filePath) + if err != nil { + if os.IsNotExist(err) { + // Ignore file not found error and return an empty list + return enabledDirs, nil + } + return nil, err + } + defer file.Close() + + // Read the file line by line + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line != "" { + //fmt.Println("Adding enabled line:", line) + //fmt.Println("Adding enabled parent:", parent) + path := filepath.Join(root, line) + fmt.Println("Adding enabled directory:", path) + enabledDirs = append(enabledDirs, path) + } + } + + // Check for scanner errors + if err := scanner.Err(); err != nil { + return nil, err + } + + return enabledDirs, nil +} diff --git a/pkg/utils/plugin_deps_test.go b/pkg/utils/plugin_deps_test.go new file mode 100644 index 000000000..c9b9e63bc --- /dev/null +++ b/pkg/utils/plugin_deps_test.go @@ -0,0 +1,76 @@ +package utils + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadPluginDeps(t *testing.T) { + // Create a temporary directory + dir := t.TempDir() + + // Create a mock "enabled" file + enabledContent := filepath.Clean("subdir1") + err := os.WriteFile(filepath.Join(dir, "enabled"), []byte(enabledContent), 0644) + assert.NoError(t, err) + + // Create subdirectories and files + subdir1 := filepath.Join(dir, "subdir1") + subdir11 := filepath.Join(subdir1, "subdir11") + subdir2 := filepath.Join(dir, "subdir2") + err = os.MkdirAll(subdir1, 0755) + assert.NoError(t, err) + err = os.MkdirAll(subdir2, 0755) + assert.NoError(t, err) + err = os.MkdirAll(subdir11, 0755) + assert.NoError(t, err) + + file0 := filepath.Join(dir, "file0.yaml") + file1 := filepath.Join(subdir1, "file1.yaml") + file2 := filepath.Join(subdir2, "file2.yaml") + file11 := filepath.Join(subdir11, "file2.yaml") + err = os.WriteFile(file1, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test1"), 0644) + assert.NoError(t, err) + err = os.WriteFile(file2, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test2"), 0644) + assert.NoError(t, err) + err = os.WriteFile(file11, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test11"), 0644) + assert.NoError(t, err) + err = os.WriteFile(file0, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test11"), 0644) + assert.NoError(t, err) + + // Call ReadPluginDeps + objects, err := ReadPluginDeps(dir) + assert.NoError(t, err) + assert.Len(t, objects, 2) + assert.Equal(t, "test1", objects[0].GetName()) + assert.Equal(t, "test11", objects[1].GetName()) + + enabledContent = filepath.Clean("") + err = os.WriteFile(filepath.Join(dir, "enabled"), []byte(enabledContent), 0644) + objects, err = ReadPluginDeps(dir) + assert.NoError(t, err) + assert.Len(t, objects, 4) + +} + +func TestReadEnabledDirs(t *testing.T) { + // Create a temporary file + dir := t.TempDir() + enabledFile := filepath.Join(dir, "enabled") + + // Write content to the file + content := "subdir1\nsubdir2\n" + err := os.WriteFile(enabledFile, []byte(content), 0644) + assert.NoError(t, err) + + // Call readEnabledDirs + enabledDirs, err := readEnabledDirs(enabledFile) + root := filepath.Dir(enabledFile) + assert.NoError(t, err) + assert.Len(t, enabledDirs, 2) + assert.Contains(t, enabledDirs, filepath.Join(root, "subdir1")) + assert.Contains(t, enabledDirs, filepath.Join(root, "subdir2")) +} diff --git a/pkg/utils/yaml.go b/pkg/utils/yaml.go new file mode 100644 index 000000000..3e0e4dd77 --- /dev/null +++ b/pkg/utils/yaml.go @@ -0,0 +1,83 @@ +package utils + +import ( + "bytes" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/yaml" +) + +func ReadYamlFilesFromDir(dir string) ([]*unstructured.Unstructured, error) { + + if !DirectoryExists(dir) { + return []*unstructured.Unstructured{}, nil + } + + var objects []*unstructured.Unstructured + err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() || !isYamlFile(d.Name()) { + return nil + } + + objs, err := ReadYamlFile(path) + if err != nil { + return fmt.Errorf("failed to read YAML file %s: %w", path, err) + } + objects = append(objects, objs...) + return nil + }) + + if err != nil { + return nil, err + } + + return objects, nil +} + +func ReadYamlFile(path string) ([]*unstructured.Unstructured, error) { + fpath := filepath.Clean(path) + if _, err := os.Stat(fpath); err != nil { + return nil, err + } + conf, err := os.ReadFile(fpath) + if err != nil { + return nil, fmt.Errorf("failed to read YAML file: %w", err) + } + + dec := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(conf), 1000) + var objects []*unstructured.Unstructured + for { + obj := &unstructured.Unstructured{} + err := dec.Decode(obj) + if err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("failed to decode YAML: %w", err) + } + objects = append(objects, obj) + } + + return objects, nil +} + +func isYamlFile(filename string) bool { + ext := filepath.Ext(filename) + return ext == ".yaml" || ext == ".yml" +} + +func DirectoryExists(path string) bool { + info, err := os.Stat(path) + if os.IsNotExist(err) { + return false + } + return info.IsDir() +} From e59b1b965ecd64b47aca3ae7512b2206c87aaa8c Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Wed, 16 Apr 2025 10:31:41 +0300 Subject: [PATCH 02/35] infra Signed-off-by: gazarenkov --- Makefile | 13 +- .../profile/backstage.io/kustomization.yaml | 32 +---- .../default-config/app-config.yaml | 0 .../default-config/db-secret.yaml | 0 .../default-config/db-service.yaml | 0 .../default-config/db-statefulset.yaml | 0 .../default-config/deployment.yaml | 0 .../{ => operator}/default-config/route.yaml | 0 .../default-config/service.yaml | 0 .../{ => operator}/namespace.yaml | 0 config/profile/rhdh/kustomization.yaml | 46 +------ .../default-config/app-config.yaml | 0 .../default-config/configmap-envs.yaml.sample | 0 .../configmap-files.yaml.sample | 0 .../default-config/db-secret.yaml | 0 .../default-config/db-service.yaml | 0 .../default-config/db-statefulset.yaml | 0 .../default-config/db-statefulset.yaml.k8s | 0 .../default-config/deployment.yaml | 0 .../default-config/deployment.yaml.k8s | 0 .../default-config/dynamic-plugins.yaml | 31 +---- .../{ => operator}/default-config/route.yaml | 0 .../default-config/secret-envs.yaml | 0 .../default-config/secret-files.yaml.sample | 0 .../default-config/service.yaml | 0 .../default-config/service.yaml.k8s | 0 .../profile/rhdh/operator/kustomization.yaml | 46 +++++++ .../rhdh/{ => operator}/namespace.yaml | 2 +- .../orchestrator/sample-greetings.yaml} | 6 +- .../plugin-deps/orchestrator/sonataflow.yaml | 124 ++++++++++++++++++ .../rhdh/patches/deployment-patch.yaml | 29 ---- .../rhdh/plugin-infra/kustomization.yaml | 8 ++ .../orchestrator/infra-serverless.yaml | 34 +++++ .../orchestrator/infra-sonataflow.yaml | 24 ++++ .../orchestrator/rbac-sonataflow.yaml | 50 +++++++ internal/controller/backstage_controller.go | 2 +- internal/controller/plugin-deps.go | 15 +-- pkg/model/dynamic-plugins.go | 38 ++++-- pkg/model/dynamic-plugins_test.go | 30 ++++- pkg/{utils => model}/plugin_deps.go | 74 ++++------- pkg/{utils => model}/plugin_deps_test.go | 55 ++++---- pkg/model/runtime.go | 5 +- pkg/model/testdata/dynamic-plugins-deps.yaml | 11 ++ pkg/utils/yaml.go | 25 +++- 44 files changed, 462 insertions(+), 238 deletions(-) rename config/profile/backstage.io/{ => operator}/default-config/app-config.yaml (100%) rename config/profile/backstage.io/{ => operator}/default-config/db-secret.yaml (100%) rename config/profile/backstage.io/{ => operator}/default-config/db-service.yaml (100%) rename config/profile/backstage.io/{ => operator}/default-config/db-statefulset.yaml (100%) rename config/profile/backstage.io/{ => operator}/default-config/deployment.yaml (100%) rename config/profile/backstage.io/{ => operator}/default-config/route.yaml (100%) rename config/profile/backstage.io/{ => operator}/default-config/service.yaml (100%) rename config/profile/backstage.io/{ => operator}/namespace.yaml (100%) rename config/profile/rhdh/{ => operator}/default-config/app-config.yaml (100%) rename config/profile/rhdh/{ => operator}/default-config/configmap-envs.yaml.sample (100%) rename config/profile/rhdh/{ => operator}/default-config/configmap-files.yaml.sample (100%) rename config/profile/rhdh/{ => operator}/default-config/db-secret.yaml (100%) rename config/profile/rhdh/{ => operator}/default-config/db-service.yaml (100%) rename config/profile/rhdh/{ => operator}/default-config/db-statefulset.yaml (100%) rename config/profile/rhdh/{ => operator}/default-config/db-statefulset.yaml.k8s (100%) rename config/profile/rhdh/{ => operator}/default-config/deployment.yaml (100%) rename config/profile/rhdh/{ => operator}/default-config/deployment.yaml.k8s (100%) rename config/profile/rhdh/{ => operator}/default-config/dynamic-plugins.yaml (72%) rename config/profile/rhdh/{ => operator}/default-config/route.yaml (100%) rename config/profile/rhdh/{ => operator}/default-config/secret-envs.yaml (100%) rename config/profile/rhdh/{ => operator}/default-config/secret-files.yaml.sample (100%) rename config/profile/rhdh/{ => operator}/default-config/service.yaml (100%) rename config/profile/rhdh/{ => operator}/default-config/service.yaml.k8s (100%) create mode 100644 config/profile/rhdh/operator/kustomization.yaml rename config/profile/rhdh/{ => operator}/namespace.yaml (94%) rename config/profile/rhdh/{default-config/plugin-deps/orchestrator/samples/greetings.yaml => operator/plugin-deps/orchestrator/sample-greetings.yaml} (94%) create mode 100644 config/profile/rhdh/operator/plugin-deps/orchestrator/sonataflow.yaml delete mode 100644 config/profile/rhdh/patches/deployment-patch.yaml create mode 100644 config/profile/rhdh/plugin-infra/kustomization.yaml create mode 100644 config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml create mode 100644 config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml create mode 100644 config/profile/rhdh/plugin-infra/orchestrator/rbac-sonataflow.yaml rename pkg/{utils => model}/plugin_deps.go (57%) rename pkg/{utils => model}/plugin_deps_test.go (62%) create mode 100644 pkg/model/testdata/dynamic-plugins-deps.yaml diff --git a/Makefile b/Makefile index 9d3a898b4..4380671fa 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ PROFILE_SHORT := $(shell echo $(PROFILE) | cut -d. -f1) # VERSION defines the project version for the bundle. # Update this value when you upgrade the version of your project. -# To re-generate a bundle for another specific version without changing the standard setup, you can: +# To re-generate a bundle for another specific version without changing the st:andard setup, you can: # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) # Set a default VERSION if it is not defined @@ -161,12 +161,14 @@ fmt: goimports ## Format the code using goimports .PHONY: test test: manifests generate fmt vet envtest $(LOCALBIN) ## Run tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path - mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config + mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/operator/default-config/* $(LOCALBIN)/default-config + mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(PKGS) -coverprofile cover.out .PHONY: integration-test integration-test: ginkgo manifests generate fmt vet envtest $(LOCALBIN) ## Run integration_tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path - mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config + mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/operator/default-config/* $(LOCALBIN)/default-config + mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" $(GINKGO) -v -r $(ARGS) integration_tests # After this time, Ginkgo will emit progress reports, so we can get visibility into long-running tests. @@ -218,9 +220,8 @@ build: manifests generate fmt vet ## Build manager binary. .PHONY: run run: manifests generate fmt vet $(LOCALBIN) ## Run a controller from your host. - mkdir -p $(LOCALBIN)/default-config/ - rm -fr $(LOCALBIN)/default-config/* - cp -r config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config/ + mkdir -p $(LOCALBIN)/default-config/ && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/operator/default-config/* $(LOCALBIN)/default-config/ + mkdir -p $(LOCALBIN)/plugin-deps/ && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps/ go run -C $(LOCALBIN) ../cmd/main.go # by default images expire from quay registry after 14 days diff --git a/config/profile/backstage.io/kustomization.yaml b/config/profile/backstage.io/kustomization.yaml index 357bf01d6..3f9818e3d 100644 --- a/config/profile/backstage.io/kustomization.yaml +++ b/config/profile/backstage.io/kustomization.yaml @@ -1,36 +1,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -# Adds namespace to all resources. -namespace: backstage-system - -# Value of this field is prepended to the -# names of all resources, e.g. a deployment named -# "wordpress" becomes "alices-wordpress". -# Note that it should also match with the prefix (text before '-') of the namespace -# field above. -namePrefix: backstage- - resources: -- ../../crd -- ../../rbac -- ../../manager -- namespace.yaml - -images: -- name: controller - newName: quay.io/rhdh-community/operator - newTag: 0.7.0 - -generatorOptions: - disableNameSuffixHash: true +- operator -configMapGenerator: -- files: - - default-config/app-config.yaml - - default-config/db-secret.yaml - - default-config/db-service.yaml - - default-config/db-statefulset.yaml - - default-config/deployment.yaml - - default-config/service.yaml - name: default-config diff --git a/config/profile/backstage.io/default-config/app-config.yaml b/config/profile/backstage.io/operator/default-config/app-config.yaml similarity index 100% rename from config/profile/backstage.io/default-config/app-config.yaml rename to config/profile/backstage.io/operator/default-config/app-config.yaml diff --git a/config/profile/backstage.io/default-config/db-secret.yaml b/config/profile/backstage.io/operator/default-config/db-secret.yaml similarity index 100% rename from config/profile/backstage.io/default-config/db-secret.yaml rename to config/profile/backstage.io/operator/default-config/db-secret.yaml diff --git a/config/profile/backstage.io/default-config/db-service.yaml b/config/profile/backstage.io/operator/default-config/db-service.yaml similarity index 100% rename from config/profile/backstage.io/default-config/db-service.yaml rename to config/profile/backstage.io/operator/default-config/db-service.yaml diff --git a/config/profile/backstage.io/default-config/db-statefulset.yaml b/config/profile/backstage.io/operator/default-config/db-statefulset.yaml similarity index 100% rename from config/profile/backstage.io/default-config/db-statefulset.yaml rename to config/profile/backstage.io/operator/default-config/db-statefulset.yaml diff --git a/config/profile/backstage.io/default-config/deployment.yaml b/config/profile/backstage.io/operator/default-config/deployment.yaml similarity index 100% rename from config/profile/backstage.io/default-config/deployment.yaml rename to config/profile/backstage.io/operator/default-config/deployment.yaml diff --git a/config/profile/backstage.io/default-config/route.yaml b/config/profile/backstage.io/operator/default-config/route.yaml similarity index 100% rename from config/profile/backstage.io/default-config/route.yaml rename to config/profile/backstage.io/operator/default-config/route.yaml diff --git a/config/profile/backstage.io/default-config/service.yaml b/config/profile/backstage.io/operator/default-config/service.yaml similarity index 100% rename from config/profile/backstage.io/default-config/service.yaml rename to config/profile/backstage.io/operator/default-config/service.yaml diff --git a/config/profile/backstage.io/namespace.yaml b/config/profile/backstage.io/operator/namespace.yaml similarity index 100% rename from config/profile/backstage.io/namespace.yaml rename to config/profile/backstage.io/operator/namespace.yaml diff --git a/config/profile/rhdh/kustomization.yaml b/config/profile/rhdh/kustomization.yaml index abdc79c59..500207b31 100644 --- a/config/profile/rhdh/kustomization.yaml +++ b/config/profile/rhdh/kustomization.yaml @@ -1,48 +1,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -# Adds namespace to all resources. -namespace: rhdh-operator - -# Value of this field is prepended to the -# names of all resources, e.g. a deployment named -# "wordpress" becomes "alices-wordpress". -# Note that it should also match with the prefix (text before '-') of the namespace -# field above. -namePrefix: rhdh- - resources: -- ../../crd -- ../../rbac -- ../../manager -- namespace.yaml - -images: -- name: controller - newName: quay.io/rhdh/rhdh-rhel9-operator - newTag: "1.7" - -patches: -- path: patches/deployment-patch.yaml - target: - kind: Deployment - name: operator - -generatorOptions: - disableNameSuffixHash: true - -configMapGenerator: -- files: - - default-config/app-config.yaml - - default-config/db-secret.yaml - - default-config/db-service.yaml - - default-config/db-statefulset.yaml - - default-config/deployment.yaml - - default-config/dynamic-plugins.yaml - - default-config/route.yaml - - default-config/secret-envs.yaml - - default-config/service.yaml - - default-config/deployment.yaml.k8s - - default-config/db-statefulset.yaml.k8s - - default-config/service.yaml.k8s - name: default-config +- operator +#- plugin-infra diff --git a/config/profile/rhdh/default-config/app-config.yaml b/config/profile/rhdh/operator/default-config/app-config.yaml similarity index 100% rename from config/profile/rhdh/default-config/app-config.yaml rename to config/profile/rhdh/operator/default-config/app-config.yaml diff --git a/config/profile/rhdh/default-config/configmap-envs.yaml.sample b/config/profile/rhdh/operator/default-config/configmap-envs.yaml.sample similarity index 100% rename from config/profile/rhdh/default-config/configmap-envs.yaml.sample rename to config/profile/rhdh/operator/default-config/configmap-envs.yaml.sample diff --git a/config/profile/rhdh/default-config/configmap-files.yaml.sample b/config/profile/rhdh/operator/default-config/configmap-files.yaml.sample similarity index 100% rename from config/profile/rhdh/default-config/configmap-files.yaml.sample rename to config/profile/rhdh/operator/default-config/configmap-files.yaml.sample diff --git a/config/profile/rhdh/default-config/db-secret.yaml b/config/profile/rhdh/operator/default-config/db-secret.yaml similarity index 100% rename from config/profile/rhdh/default-config/db-secret.yaml rename to config/profile/rhdh/operator/default-config/db-secret.yaml diff --git a/config/profile/rhdh/default-config/db-service.yaml b/config/profile/rhdh/operator/default-config/db-service.yaml similarity index 100% rename from config/profile/rhdh/default-config/db-service.yaml rename to config/profile/rhdh/operator/default-config/db-service.yaml diff --git a/config/profile/rhdh/default-config/db-statefulset.yaml b/config/profile/rhdh/operator/default-config/db-statefulset.yaml similarity index 100% rename from config/profile/rhdh/default-config/db-statefulset.yaml rename to config/profile/rhdh/operator/default-config/db-statefulset.yaml diff --git a/config/profile/rhdh/default-config/db-statefulset.yaml.k8s b/config/profile/rhdh/operator/default-config/db-statefulset.yaml.k8s similarity index 100% rename from config/profile/rhdh/default-config/db-statefulset.yaml.k8s rename to config/profile/rhdh/operator/default-config/db-statefulset.yaml.k8s diff --git a/config/profile/rhdh/default-config/deployment.yaml b/config/profile/rhdh/operator/default-config/deployment.yaml similarity index 100% rename from config/profile/rhdh/default-config/deployment.yaml rename to config/profile/rhdh/operator/default-config/deployment.yaml diff --git a/config/profile/rhdh/default-config/deployment.yaml.k8s b/config/profile/rhdh/operator/default-config/deployment.yaml.k8s similarity index 100% rename from config/profile/rhdh/default-config/deployment.yaml.k8s rename to config/profile/rhdh/operator/default-config/deployment.yaml.k8s diff --git a/config/profile/rhdh/default-config/dynamic-plugins.yaml b/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml similarity index 72% rename from config/profile/rhdh/default-config/dynamic-plugins.yaml rename to config/profile/rhdh/operator/default-config/dynamic-plugins.yaml index db4737c8f..69dfe701c 100644 --- a/config/profile/rhdh/default-config/dynamic-plugins.yaml +++ b/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml @@ -25,18 +25,20 @@ kind: ConfigMap metadata: name: default-dynamic-plugins data: + plugin-dependencies: | + # orchestrator dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml plugins: - - disabled: false + - disabled: true #false integrity: sha512-2aOHDLFrGMAtyHFiyGZwVBZ9Op+TmKYUwfZxwoaGJ1s6JSy/0qgqineEEE0K3dn/f17XBUj+H1dwa5Al598Ugw== package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-backend-dynamic-1.4.0.tgz pluginConfig: orchestrator: dataIndexService: url: http://sonataflow-platform-data-index-service - - disabled: false + - disabled: true #false integrity: sha512-2yasbfBZ3iKntArIfK+hk9tvv4b/dy9+WKXOcWIotqkI1gv+Nhvy+m55KAUWi2vmfM0rj3EoG6YP+3Zajn1KyA== package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-1.4.0.tgz pluginConfig: @@ -54,28 +56,3 @@ data: text: Orchestrator module: OrchestratorPlugin path: /orchestrator -# - disabled: true -# package: ./dynamic-plugins/dist/backstage-plugin-notifications -# pluginConfig: -# dynamicPlugins: -# frontend: -# backstage.plugin-notifications: -# dynamicRoutes: -# - importName: NotificationsPage -# menuItem: -# config: -# props: -# titleCounterEnabled: true -# webNotificationsEnabled: false -# importName: NotificationsSidebarItem -# path: /notifications -# - disabled: true -# package: ./dynamic-plugins/dist/backstage-plugin-signals -# pluginConfig: -# dynamicPlugins: -# frontend: -# backstage.plugin-signals: {} -# - disabled: true -# package: ./dynamic-plugins/dist/backstage-plugin-notifications-backend-dynamic -# - disabled: true -# package: ./dynamic-plugins/dist/backstage-plugin-signals-backend-dynamic \ No newline at end of file diff --git a/config/profile/rhdh/default-config/route.yaml b/config/profile/rhdh/operator/default-config/route.yaml similarity index 100% rename from config/profile/rhdh/default-config/route.yaml rename to config/profile/rhdh/operator/default-config/route.yaml diff --git a/config/profile/rhdh/default-config/secret-envs.yaml b/config/profile/rhdh/operator/default-config/secret-envs.yaml similarity index 100% rename from config/profile/rhdh/default-config/secret-envs.yaml rename to config/profile/rhdh/operator/default-config/secret-envs.yaml diff --git a/config/profile/rhdh/default-config/secret-files.yaml.sample b/config/profile/rhdh/operator/default-config/secret-files.yaml.sample similarity index 100% rename from config/profile/rhdh/default-config/secret-files.yaml.sample rename to config/profile/rhdh/operator/default-config/secret-files.yaml.sample diff --git a/config/profile/rhdh/default-config/service.yaml b/config/profile/rhdh/operator/default-config/service.yaml similarity index 100% rename from config/profile/rhdh/default-config/service.yaml rename to config/profile/rhdh/operator/default-config/service.yaml diff --git a/config/profile/rhdh/default-config/service.yaml.k8s b/config/profile/rhdh/operator/default-config/service.yaml.k8s similarity index 100% rename from config/profile/rhdh/default-config/service.yaml.k8s rename to config/profile/rhdh/operator/default-config/service.yaml.k8s diff --git a/config/profile/rhdh/operator/kustomization.yaml b/config/profile/rhdh/operator/kustomization.yaml new file mode 100644 index 000000000..f6148285a --- /dev/null +++ b/config/profile/rhdh/operator/kustomization.yaml @@ -0,0 +1,46 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: rhdh-operator + +namePrefix: rhdh- + +resources: +- ../../../crd +- ../../../rbac +- ../../../manager +- namespace.yaml + + +images: +- name: controller + newName: quay.io/gazarenk/backstage-operator + +patches: +- path: patches/deployment-patch.yaml + target: + kind: Deployment + name: operator + +generatorOptions: + disableNameSuffixHash: true + +configMapGenerator: + - files: + - default-config/app-config.yaml + - default-config/db-secret.yaml + - default-config/db-service.yaml + - default-config/db-statefulset.yaml + - default-config/deployment.yaml + - default-config/dynamic-plugins.yaml + - default-config/route.yaml + - default-config/secret-envs.yaml + - default-config/service.yaml + - default-config/deployment.yaml.k8s + - default-config/db-statefulset.yaml.k8s + - default-config/service.yaml.k8s + name: default-config + - files: + - plugin-deps/orchestrator/sonataflow.yaml + - plugin-deps/orchestrator/sample-greetings.yaml + name: plugin-deps diff --git a/config/profile/rhdh/namespace.yaml b/config/profile/rhdh/operator/namespace.yaml similarity index 94% rename from config/profile/rhdh/namespace.yaml rename to config/profile/rhdh/operator/namespace.yaml index db987089f..2bbc54ecd 100644 --- a/config/profile/rhdh/namespace.yaml +++ b/config/profile/rhdh/operator/namespace.yaml @@ -9,4 +9,4 @@ metadata: app.kubernetes.io/created-by: backstage-operator app.kubernetes.io/part-of: backstage-operator app.kubernetes.io/managed-by: kustomize - name: operator + name: rhdh-operator diff --git a/config/profile/rhdh/default-config/plugin-deps/orchestrator/samples/greetings.yaml b/config/profile/rhdh/operator/plugin-deps/orchestrator/sample-greetings.yaml similarity index 94% rename from config/profile/rhdh/default-config/plugin-deps/orchestrator/samples/greetings.yaml rename to config/profile/rhdh/operator/plugin-deps/orchestrator/sample-greetings.yaml index e4955b6c9..7c92f2c23 100644 --- a/config/profile/rhdh/default-config/plugin-deps/orchestrator/samples/greetings.yaml +++ b/config/profile/rhdh/operator/plugin-deps/orchestrator/sample-greetings.yaml @@ -116,12 +116,12 @@ spec: persistence: postgresql: secretRef: - name: backstage-psql-secret-bs1 # hardcoded backstage-psql-secret-{{cr-name}} + name: backstage-psql-secret-{{backstage-name}} userKey: POSTGRES_USER #hardcoded passwordKey: POSTGRES_PASSWORD #hardcoded serviceRef: - name: backstage-psql-bs1 # hardcoded backstage-psql-{{cr-name}} - namespace: backstage # hardcoded {{cr-namespace}} + name: backstage-psql-{{backstage-name}} # hardcoded backstage-psql-{{cr-name}} + namespace: {{backstage-ns}} # hardcoded {{cr-namespace}} databaseName: backstage_plugin_orchestrator #hardcoded port: 5432 databaseSchema: greeting diff --git a/config/profile/rhdh/operator/plugin-deps/orchestrator/sonataflow.yaml b/config/profile/rhdh/operator/plugin-deps/orchestrator/sonataflow.yaml new file mode 100644 index 000000000..090cba636 --- /dev/null +++ b/config/profile/rhdh/operator/plugin-deps/orchestrator/sonataflow.yaml @@ -0,0 +1,124 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-knative-to-sonataflow-and-workflows # hardcoded +spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow knative events to be delivered to workflows. + kubernetes.io/metadata.name: knative-eventing + - namespaceSelector: + matchLabels: + # Allow auxiliary knative function for workflow (such as m2k-save-transformation) + kubernetes.io/metadata.name: knative-serving +--- +# NetworkPolicy to unblock incoming traffic to the namespace +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-external-communication # hardcoded +spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow knative events to be delivered to workflows. + kubernetes.io/metadata.name: openshift-ingress +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-intra-network # hardcoded +spec: + # Apply this policy to all pods in the namespace + podSelector: {} + # Specify policy type as 'Ingress' to control incoming traffic rules + policyTypes: + - Ingress + ingress: + - from: + # Allow ingress from any pod within the same namespace + - podSelector: {} +--- +# NetworkPolicy to allow openshift-user-workload-monitoring pods to access all pods within the workflow's namespace +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-monitoring-to-sonataflow-and-workflows # hardcoded +spec: + # Apply this policy to all pods in the namespace + podSelector: {} + # Specify policy type as 'Ingress' to control incoming traffic rules + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow openshift-user-workload-monitoring pods to access the workflow. + kubernetes.io/metadata.name: openshift-user-workload-monitoring +--- +apiVersion: operator.knative.dev/v1beta1 +kind: KnativeEventing +metadata: + name: knative-eventing + namespace: knative-eventing +spec: + Registry: {} +--- +apiVersion: operator.knative.dev/v1beta1 +kind: KnativeServing +metadata: + name: knative-serving + namespace: knative-serving +spec: + controller-custom-certs: + name: "" + type: "" + registry: {} +--- +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowPlatform +metadata: + name: sonataflow-platform # hardcoded +spec: + monitoring: + enabled: true + build: + template: + resources: + requests: + memory: 64Mi + cpu: 250m + limits: + memory: 1Gi + cpu: 500m + services: + dataIndex: + enabled: true + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} # hardcoded backstage-psql-secret-{{cr-name}} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: backstage-psql-{{backstage-name}} # hardcoded backstage-psql-{{cr-name}} + namespace: {{backstage-ns}} # hardcoded {{cr-namespace}} + databaseName: backstage_plugin_orchestrator #hardcoded + jobService: + enabled: true + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} # hardcoded backstage-psql-secret-{{cr-name}} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: backstage-psql-{{backstage-name}} # hardcoded backstage-psql-{{cr-name}} + namespace: {{backstage-ns}} # hardcoded {{cr-namespace}} + databaseName: backstage_plugin_orchestrator # hardcoded name of DB created by orchestrator plugin diff --git a/config/profile/rhdh/patches/deployment-patch.yaml b/config/profile/rhdh/patches/deployment-patch.yaml deleted file mode 100644 index a4097939c..000000000 --- a/config/profile/rhdh/patches/deployment-patch.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: operator - labels: - app: rhdh-operator -spec: - selector: - matchLabels: - app: rhdh-operator - template: - metadata: - labels: - app: rhdh-operator - app.kubernetes.io/component: rhdh-operator - spec: - containers: - - name: manager - env: - - name: OPERATOR_NAME - value: rhdh-operator - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: RELATED_IMAGE_postgresql - value: quay.io/fedora/postgresql-15:latest - - name: RELATED_IMAGE_backstage - value: quay.io/rhdh/rhdh-hub-rhel9:next diff --git a/config/profile/rhdh/plugin-infra/kustomization.yaml b/config/profile/rhdh/plugin-infra/kustomization.yaml new file mode 100644 index 000000000..34b1b9cb8 --- /dev/null +++ b/config/profile/rhdh/plugin-infra/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- orchestrator/rbac-sonataflow.yaml +- orchestrator/infra-sonataflow.yaml +- orchestrator/infra-serverless.yaml + diff --git a/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml b/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml new file mode 100644 index 000000000..dc86013e4 --- /dev/null +++ b/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml @@ -0,0 +1,34 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-serverless +--- +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: serverless-operator-group + namespace: openshift-serverless +spec: +--- +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: serverless-operator + namespace: openshift-serverless +spec: + channel: stable # channel of an operator package to subscribe to + installPlanApproval: Automatic #Manual # whether the update should be installed automatically + name: serverless-operator # name of the operator package + source: redhat-operators # name of the catalog source + sourceNamespace: openshift-marketplace +--- +apiVersion: v1 +kind: Namespace +metadata: + name: knative-serving +--- +apiVersion: v1 +kind: Namespace +metadata: + name: knative-eventing + diff --git a/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml b/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml new file mode 100644 index 000000000..7e8e44f1c --- /dev/null +++ b/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-serverless-logic +--- +apiVersion: operators.coreos.com/v1 +kind: OperatorGroup +metadata: + name: openshift-serverless-logic + namespace: openshift-serverless-logic +spec: +--- +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: logic-operator-rhel8 + namespace: openshift-serverless-logic +spec: + channel: alpha # channel of an operator package to subscribe to + installPlanApproval: Automatic # Manual # whether the update should be installed automatically + name: logic-operator-rhel8 # name of the operator package + source: redhat-operators # name of the catalog source + sourceNamespace: openshift-marketplace + startingCSV: logic-operator-rhel8.v1.35.0 # The initial version of the operator \ No newline at end of file diff --git a/config/profile/rhdh/plugin-infra/orchestrator/rbac-sonataflow.yaml b/config/profile/rhdh/plugin-infra/orchestrator/rbac-sonataflow.yaml new file mode 100644 index 000000000..92be1d5ec --- /dev/null +++ b/config/profile/rhdh/plugin-infra/orchestrator/rbac-sonataflow.yaml @@ -0,0 +1,50 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: manager-sonataflow-role +rules: + - apiGroups: + - "sonataflow.org" + resources: + - sonataflowplatforms + - sonataflows + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "networking.k8s.io" + resources: + - networkpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: backstage-operator + app.kubernetes.io/instance: manager-rolebinding + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/part-of: backstage-operator + name: manager-sonataflow-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: rhdh-manager-sonataflow-role +subjects: + - kind: ServiceAccount + name: rhdh-controller-manager + namespace: rhdh-operator diff --git a/internal/controller/backstage_controller.go b/internal/controller/backstage_controller.go index 1e2a5d07f..42516ccd6 100644 --- a/internal/controller/backstage_controller.go +++ b/internal/controller/backstage_controller.go @@ -107,7 +107,7 @@ func (r *BackstageReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } // Apply the plugin dependencies - if err := r.applyPluginDeps(ctx, backstage.Namespace); err != nil { + if err := r.applyPluginDeps(ctx, req.NamespacedName, *bsModel.DynamicPlugins); err != nil { return ctrl.Result{}, errorAndStatus(&backstage, "failed to apply plugin dependencies", err) } diff --git a/internal/controller/plugin-deps.go b/internal/controller/plugin-deps.go index 71433cf7c..038e3f811 100644 --- a/internal/controller/plugin-deps.go +++ b/internal/controller/plugin-deps.go @@ -4,27 +4,24 @@ import ( "context" "errors" "fmt" + "github.com/redhat-developer/rhdh-operator/pkg/model" + "k8s.io/apimachinery/pkg/types" "os" "path/filepath" "strings" - "github.com/redhat-developer/rhdh-operator/pkg/utils" - "sigs.k8s.io/controller-runtime/pkg/log" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" ) -func (r *BackstageReconciler) applyPluginDeps(ctx context.Context, bsNamespace string) error { +func (r *BackstageReconciler) applyPluginDeps(ctx context.Context, nsName types.NamespacedName, plugins model.DynamicPlugins) error { lg := log.FromContext(ctx) - dir := filepath.Join(os.Getenv("LOCALBIN"), "default-config", "plugin-deps") - - // Read all YAML files from the directory - //objects, err := utils.ReadYamlFilesFromDir(dir) - objects, err := utils.ReadPluginDeps(dir) + dir := filepath.Join(os.Getenv("LOCALBIN"), "plugin-deps") + objects, err := model.ReadPluginDeps(dir, nsName.Name, nsName.Namespace, plugins.Dependencies()) if err != nil { return fmt.Errorf("failed to read YAML files: %w", err) } @@ -37,7 +34,7 @@ func (r *BackstageReconciler) applyPluginDeps(ctx context.Context, bsNamespace s // Set the namespace if not set if obj.GetNamespace() == "" { - obj.SetNamespace(bsNamespace) + obj.SetNamespace(nsName.Namespace) } if err := r.Patch(ctx, obj, client.Apply, &client.PatchOptions{FieldManager: BackstageFieldManager, Force: ptr.To(true)}); err != nil { diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index d40766b6b..c2c35edf2 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -3,6 +3,7 @@ package model import ( "fmt" "os" + "strings" "golang.org/x/exp/maps" @@ -60,15 +61,19 @@ func addDynamicPluginsFromSpec(spec bsv1.BackstageSpec, model *BackstageModel) e } dp := model.ExternalConfig.DynamicPlugins - if dp.Data == nil || len(dp.Data) != 1 || dp.Data[DynamicPluginsFile] == "" { - return fmt.Errorf("dynamic plugin configMap expects exactly one Data key named '%s' ", DynamicPluginsFile) + + if dp.Data == nil || (dp.Data[DynamicPluginsFile] == "" && dp.Data[EnabledPluginsDepsFile] == "") { + return fmt.Errorf("dynamic plugin configMap expects '%s' and|or '%s' Data keys", DynamicPluginsFile, EnabledPluginsDepsFile) } - model.backstageDeployment.mountFilesFrom([]string{dynamicPluginInitContainerName}, ConfigMapObjectKind, - dp.Name, ic.WorkingDir, DynamicPluginsFile, true, maps.Keys(dp.Data)) + if dp.Data[DynamicPluginsFile] != "" { + model.backstageDeployment.mountFilesFrom([]string{dynamicPluginInitContainerName}, ConfigMapObjectKind, + dp.Name, ic.WorkingDir, DynamicPluginsFile, true, maps.Keys(dp.Data)) + } - return nil + model.DynamicPlugins.ConfigMap = &dp + return nil } // implementation of RuntimeObject interface @@ -92,16 +97,25 @@ func (p *DynamicPlugins) EmptyObject() client.Object { // implementation of RuntimeObject interface func (p *DynamicPlugins) addToModel(model *BackstageModel, backstage bsv1.Backstage) (bool, error) { - if p.ConfigMap == nil || (backstage.Spec.Application != nil && backstage.Spec.Application.DynamicPluginsConfigMapName != "") { - return false, nil + if p.ConfigMap == nil { + if backstage.Spec.Application != nil && backstage.Spec.Application.DynamicPluginsConfigMapName != "" { + p.ConfigMap = &corev1.ConfigMap{} + } else { + return false, nil + } } model.setRuntimeObject(p) + model.DynamicPlugins = p return true, nil } // implementation of RuntimeObject interface // ConfigMap name must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.ConfigMap.name -func (p *DynamicPlugins) updateAndValidate(model *BackstageModel, _ bsv1.Backstage) error { +func (p *DynamicPlugins) updateAndValidate(model *BackstageModel, backstage bsv1.Backstage) error { + + if backstage.Spec.Application != nil && backstage.Spec.Application.DynamicPluginsConfigMapName != "" { + return nil + } _, initContainer := DynamicPluginsInitContainer(model.backstageDeployment.deployment.Spec.Template.Spec.InitContainers) if initContainer == nil { @@ -128,6 +142,14 @@ func (p *DynamicPlugins) setMetaInfo(backstage bsv1.Backstage, scheme *runtime.S setMetaInfo(p.ConfigMap, backstage, scheme) } +func (p *DynamicPlugins) Dependencies() []string { + data := p.ConfigMap.Data[EnabledPluginsDepsFile] + if data != "" { + return strings.Split(data, "\n") + } + return []string{} +} + // returns initContainer supposed to initialize DynamicPlugins // TODO consider to use a label to identify instead func DynamicPluginsInitContainer(initContainers []corev1.Container) (int, *corev1.Container) { diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index d0ab040c0..618f079dc 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -60,7 +60,8 @@ func TestDynamicPluginsInvalidKeyName(t *testing.T) { _, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, false, testObj.scheme) assert.Error(t, err) - assert.Contains(t, err.Error(), "expects exactly one Data key named 'dynamic-plugins.yaml'") + //assert.Contains(t, err.Error(), "expects exactly one Data key named 'dynamic-plugins.yaml'") + assert.Contains(t, err.Error(), "dynamic plugin configMap expects 'dynamic-plugins.yaml' and|or 'plugin-dependencies' Data keys") } @@ -91,6 +92,8 @@ func TestDefaultDynamicPlugins(t *testing.T) { //vol-default-dynamic-plugins assert.Equal(t, 4, len(ic.VolumeMounts)) + assert.Equal(t, 0, len(model.DynamicPlugins.Dependencies())) + } func TestDefaultAndSpecifiedDynamicPlugins(t *testing.T) { @@ -120,6 +123,8 @@ func TestDefaultAndSpecifiedDynamicPlugins(t *testing.T) { //vol-dplugin assert.Equal(t, 4, len(ic.VolumeMounts)) assert.Equal(t, utils.GenerateVolumeNameFromCmOrSecret("dplugin"), ic.VolumeMounts[3].Name) + + assert.Equal(t, 0, len(model.DynamicPlugins.Dependencies())) } func TestDynamicPluginsFailOnArbitraryDepl(t *testing.T) { @@ -152,6 +157,29 @@ func TestNotConfiguredDPsNotInTheModel(t *testing.T) { } } +func TestWithDynamicPluginsDeps(t *testing.T) { + + bs := testDynamicPluginsBackstage.DeepCopy() + bs.Spec.Application.DynamicPluginsConfigMapName = "dplugin" + + testObj := createBackstageTest(*bs).withDefaultConfig(true). + addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). + addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") + + testObj.externalConfig.DynamicPlugins = corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "dplugin"}, + Data: map[string]string{DynamicPluginsFile: "tt", EnabledPluginsDepsFile: `dep1 +dep2`}, + } + + model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, false, testObj.scheme) + + assert.NoError(t, err) + assert.NotNil(t, model) + assert.Equal(t, 2, len(model.DynamicPlugins.Dependencies())) + +} + func initContainer(model *BackstageModel) *corev1.Container { for _, v := range model.backstageDeployment.deployment.Spec.Template.Spec.InitContainers { if v.Name == dynamicPluginInitContainerName { diff --git a/pkg/utils/plugin_deps.go b/pkg/model/plugin_deps.go similarity index 57% rename from pkg/utils/plugin_deps.go rename to pkg/model/plugin_deps.go index 8b5227871..a0ddc5bb9 100644 --- a/pkg/utils/plugin_deps.go +++ b/pkg/model/plugin_deps.go @@ -1,29 +1,28 @@ -package utils +package model import ( - "bufio" "fmt" "os" "path/filepath" "strings" + "github.com/redhat-developer/rhdh-operator/pkg/utils" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func ReadPluginDeps(rootDir string) ([]*unstructured.Unstructured, error) { +const EnabledPluginsDepsFile = "plugin-dependencies" + +// ReadPluginDeps reads the plugin dependencies from the specified directory +// and returns a slice of unstructured.Unstructured objects. +func ReadPluginDeps(rootDir, bsName, bsNamespace string, enabledDirs []string) ([]*unstructured.Unstructured, error) { - if !DirectoryExists(rootDir) { + if !utils.DirectoryExists(rootDir) { return []*unstructured.Unstructured{}, nil } var objects []*unstructured.Unstructured - // Read allowed directories from the "enabled" file - enabledDirs, err := readEnabledDirs(filepath.Join(rootDir, "enabled")) - if err != nil { - return nil, err - } - // Read the directory tree files, err := processDepsTree(rootDir, enabledDirs) @@ -32,10 +31,23 @@ func ReadPluginDeps(rootDir string) ([]*unstructured.Unstructured, error) { } for _, file := range files { - if !isYamlFile(file) { + if !utils.IsYamlFile(file) { continue } - objs, err := ReadYamlFile(file) + + // Read file content + content, err := os.ReadFile(file) + if err != nil { + return nil, fmt.Errorf("failed to read file %s: %w", file, err) + } + + // Perform substitutions + modifiedContent := strings.ReplaceAll(string(content), "{{backstage-name}}", bsName) + modifiedContent = strings.ReplaceAll(modifiedContent, "{{backstage-ns}}", bsNamespace) + + // Parse the modified content + objs, err := utils.ReadYamlContent(modifiedContent) + if err != nil { return nil, fmt.Errorf("failed to read YAML file %s: %w", file, err) } @@ -49,7 +61,7 @@ func processDepsTree(root string, enabledDirs []string) ([]string, error) { // Normalize and store allowed directories enabledMap := make(map[string]bool) for _, dir := range enabledDirs { - enabledMap[filepath.Clean(dir)] = true + enabledMap[filepath.Join(root, dir)] = true } files := []string{} @@ -96,39 +108,3 @@ func isEnabled(path string, enabledMap map[string]bool) bool { } return false } - -func readEnabledDirs(filePath string) ([]string, error) { - var enabledDirs []string - - root := filepath.Dir(filePath) - // Open the file - file, err := os.Open(filePath) - if err != nil { - if os.IsNotExist(err) { - // Ignore file not found error and return an empty list - return enabledDirs, nil - } - return nil, err - } - defer file.Close() - - // Read the file line by line - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line != "" { - //fmt.Println("Adding enabled line:", line) - //fmt.Println("Adding enabled parent:", parent) - path := filepath.Join(root, line) - fmt.Println("Adding enabled directory:", path) - enabledDirs = append(enabledDirs, path) - } - } - - // Check for scanner errors - if err := scanner.Err(); err != nil { - return nil, err - } - - return enabledDirs, nil -} diff --git a/pkg/utils/plugin_deps_test.go b/pkg/model/plugin_deps_test.go similarity index 62% rename from pkg/utils/plugin_deps_test.go rename to pkg/model/plugin_deps_test.go index c9b9e63bc..e9b72698d 100644 --- a/pkg/utils/plugin_deps_test.go +++ b/pkg/model/plugin_deps_test.go @@ -1,4 +1,4 @@ -package utils +package model import ( "os" @@ -9,19 +9,14 @@ import ( ) func TestReadPluginDeps(t *testing.T) { - // Create a temporary directory - dir := t.TempDir() - // Create a mock "enabled" file - enabledContent := filepath.Clean("subdir1") - err := os.WriteFile(filepath.Join(dir, "enabled"), []byte(enabledContent), 0644) - assert.NoError(t, err) + dir := t.TempDir() // Create subdirectories and files subdir1 := filepath.Join(dir, "subdir1") subdir11 := filepath.Join(subdir1, "subdir11") subdir2 := filepath.Join(dir, "subdir2") - err = os.MkdirAll(subdir1, 0755) + err := os.MkdirAll(subdir1, 0755) assert.NoError(t, err) err = os.MkdirAll(subdir2, 0755) assert.NoError(t, err) @@ -42,35 +37,45 @@ func TestReadPluginDeps(t *testing.T) { assert.NoError(t, err) // Call ReadPluginDeps - objects, err := ReadPluginDeps(dir) + objects, err := ReadPluginDeps(dir, "", "", []string{"subdir1"}) assert.NoError(t, err) assert.Len(t, objects, 2) assert.Equal(t, "test1", objects[0].GetName()) assert.Equal(t, "test11", objects[1].GetName()) - enabledContent = filepath.Clean("") - err = os.WriteFile(filepath.Join(dir, "enabled"), []byte(enabledContent), 0644) - objects, err = ReadPluginDeps(dir) + objects, err = ReadPluginDeps(dir, "", "", []string{""}) assert.NoError(t, err) assert.Len(t, objects, 4) - } -func TestReadEnabledDirs(t *testing.T) { - // Create a temporary file +func TestReadPluginDepsSubstitutions(t *testing.T) { + dir := t.TempDir() - enabledFile := filepath.Join(dir, "enabled") - // Write content to the file - content := "subdir1\nsubdir2\n" - err := os.WriteFile(enabledFile, []byte(content), 0644) + // Create subdirectory and a YAML file with placeholders + subdir1 := filepath.Join(dir, "subdir1") + err := os.MkdirAll(subdir1, 0755) assert.NoError(t, err) - // Call readEnabledDirs - enabledDirs, err := readEnabledDirs(enabledFile) - root := filepath.Dir(enabledFile) + file1 := filepath.Join(subdir1, "file1.yaml") + yamlContent := ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{backstage-name}} + namespace: {{backstage-ns}} +` + err = os.WriteFile(file1, []byte(yamlContent), 0644) assert.NoError(t, err) - assert.Len(t, enabledDirs, 2) - assert.Contains(t, enabledDirs, filepath.Join(root, "subdir1")) - assert.Contains(t, enabledDirs, filepath.Join(root, "subdir2")) + + // Call ReadPluginDeps with substitution values + bsName := "test-name" + bsNamespace := "test-namespace" + objects, err := ReadPluginDeps(dir, bsName, bsNamespace, []string{"subdir1"}) + assert.NoError(t, err) + assert.Len(t, objects, 1) + + // Verify the substitutions + assert.Equal(t, "test-name", objects[0].GetName()) + assert.Equal(t, "test-namespace", objects[0].GetNamespace()) } diff --git a/pkg/model/runtime.go b/pkg/model/runtime.go index e8f445391..7679dd4fa 100644 --- a/pkg/model/runtime.go +++ b/pkg/model/runtime.go @@ -43,8 +43,9 @@ type BackstageModel struct { LocalDbService *DbService LocalDbSecret *DbSecret - route *BackstageRoute - appConfig *AppConfig + route *BackstageRoute + appConfig *AppConfig + DynamicPlugins *DynamicPlugins RuntimeObjects []RuntimeObject diff --git a/pkg/model/testdata/dynamic-plugins-deps.yaml b/pkg/model/testdata/dynamic-plugins-deps.yaml new file mode 100644 index 000000000..3dd06ab15 --- /dev/null +++ b/pkg/model/testdata/dynamic-plugins-deps.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: default-dynamic-plugins # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name +data: + "enabled-plugins-deps.txt": | + orchestrator/* + "dynamic-plugins.yaml": | + includes: + - dynamic-plugins.default.yaml + plugins: [] \ No newline at end of file diff --git a/pkg/utils/yaml.go b/pkg/utils/yaml.go index 3e0e4dd77..524b5f0ca 100644 --- a/pkg/utils/yaml.go +++ b/pkg/utils/yaml.go @@ -23,7 +23,7 @@ func ReadYamlFilesFromDir(dir string) ([]*unstructured.Unstructured, error) { if err != nil { return err } - if d.IsDir() || !isYamlFile(d.Name()) { + if d.IsDir() || !IsYamlFile(d.Name()) { return nil } @@ -69,7 +69,28 @@ func ReadYamlFile(path string) ([]*unstructured.Unstructured, error) { return objects, nil } -func isYamlFile(filename string) bool { +func ReadYamlContent(content string) ([]*unstructured.Unstructured, error) { + // Create a YAML decoder from the content + dec := yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(content)), 1000) + var objects []*unstructured.Unstructured + + // Decode the content into unstructured objects + for { + obj := &unstructured.Unstructured{} + err := dec.Decode(obj) + if err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("failed to decode YAML content: %w", err) + } + objects = append(objects, obj) + } + + return objects, nil +} + +func IsYamlFile(filename string) bool { ext := filepath.Ext(filename) return ext == ".yaml" || ext == ".yml" } From 7afab0812273b7af92dee1eb0f204786f00bd924 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Wed, 16 Apr 2025 11:46:11 +0300 Subject: [PATCH 03/35] fix no plugin-deps dir Signed-off-by: gazarenkov --- Makefile | 4 +-- .../backstage.io/operator/kustomization.yaml | 30 +++++++++++++++++++ internal/controller/plugin-deps.go | 5 ++-- pkg/model/dynamic-plugins.go | 3 ++ pkg/model/runtime.go | 2 +- 5 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 config/profile/backstage.io/operator/kustomization.yaml diff --git a/Makefile b/Makefile index 4380671fa..f1b8a2954 100644 --- a/Makefile +++ b/Makefile @@ -162,13 +162,13 @@ fmt: goimports ## Format the code using goimports .PHONY: test test: manifests generate fmt vet envtest $(LOCALBIN) ## Run tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/operator/default-config/* $(LOCALBIN)/default-config - mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps + mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps 2>/dev/null || : LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(PKGS) -coverprofile cover.out .PHONY: integration-test integration-test: ginkgo manifests generate fmt vet envtest $(LOCALBIN) ## Run integration_tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/operator/default-config/* $(LOCALBIN)/default-config - mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps + mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps 2>/dev/null || : LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" $(GINKGO) -v -r $(ARGS) integration_tests # After this time, Ginkgo will emit progress reports, so we can get visibility into long-running tests. diff --git a/config/profile/backstage.io/operator/kustomization.yaml b/config/profile/backstage.io/operator/kustomization.yaml new file mode 100644 index 000000000..e38c41f7f --- /dev/null +++ b/config/profile/backstage.io/operator/kustomization.yaml @@ -0,0 +1,30 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: backstage-system + +namePrefix: backstage- + +resources: +- ../../../crd +- ../../../rbac +- ../../../manager +- namespace.yaml + +images: +- name: controller + newName: quay.io/rhdh-community/operator + newTag: 0.7.0 + +generatorOptions: + disableNameSuffixHash: true + +configMapGenerator: +- files: + - default-config/app-config.yaml + - default-config/db-secret.yaml + - default-config/db-service.yaml + - default-config/db-statefulset.yaml + - default-config/deployment.yaml + - default-config/service.yaml + name: default-config diff --git a/internal/controller/plugin-deps.go b/internal/controller/plugin-deps.go index 038e3f811..c879a9133 100644 --- a/internal/controller/plugin-deps.go +++ b/internal/controller/plugin-deps.go @@ -4,12 +4,13 @@ import ( "context" "errors" "fmt" - "github.com/redhat-developer/rhdh-operator/pkg/model" - "k8s.io/apimachinery/pkg/types" "os" "path/filepath" "strings" + "github.com/redhat-developer/rhdh-operator/pkg/model" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/log" "k8s.io/utils/ptr" diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index c2c35edf2..e47dc98e8 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -143,6 +143,9 @@ func (p *DynamicPlugins) setMetaInfo(backstage bsv1.Backstage, scheme *runtime.S } func (p *DynamicPlugins) Dependencies() []string { + if p.ConfigMap == nil { + return []string{} + } data := p.ConfigMap.Data[EnabledPluginsDepsFile] if data != "" { return strings.Split(data, "\n") diff --git a/pkg/model/runtime.go b/pkg/model/runtime.go index 7679dd4fa..7f03034c2 100644 --- a/pkg/model/runtime.go +++ b/pkg/model/runtime.go @@ -102,7 +102,7 @@ func InitObjects(ctx context.Context, backstage bsv1.Backstage, externalConfig E lg := log.FromContext(ctx) lg.V(1) - model := &BackstageModel{RuntimeObjects: make([]RuntimeObject, 0), ExternalConfig: externalConfig, localDbEnabled: backstage.Spec.IsLocalDbEnabled(), isOpenshift: isOpenshift} + model := &BackstageModel{RuntimeObjects: make([]RuntimeObject, 0), ExternalConfig: externalConfig, localDbEnabled: backstage.Spec.IsLocalDbEnabled(), isOpenshift: isOpenshift, DynamicPlugins: &DynamicPlugins{}} // looping through the registered runtimeConfig objects initializing the model for _, conf := range runtimeConfig { From 2a4d5916a321c03d6205645bc1ee8b2edc8d36da Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Wed, 16 Apr 2025 13:30:34 +0300 Subject: [PATCH 04/35] no traversing on plugin-deps dir Signed-off-by: gazarenkov --- pkg/model/dynamic-plugins.go | 1 + pkg/model/plugin_deps.go | 62 +++++++++-------------------------- pkg/model/plugin_deps_test.go | 21 ++++++++---- 3 files changed, 31 insertions(+), 53 deletions(-) diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index e47dc98e8..ba5fab476 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -30,6 +30,7 @@ import ( const dynamicPluginInitContainerName = "install-dynamic-plugins" const DynamicPluginsFile = "dynamic-plugins.yaml" +const EnabledPluginsDepsFile = "plugin-dependencies" type DynamicPluginsFactory struct{} diff --git a/pkg/model/plugin_deps.go b/pkg/model/plugin_deps.go index a0ddc5bb9..dd49f6f3e 100644 --- a/pkg/model/plugin_deps.go +++ b/pkg/model/plugin_deps.go @@ -11,8 +11,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -const EnabledPluginsDepsFile = "plugin-dependencies" - // ReadPluginDeps reads the plugin dependencies from the specified directory // and returns a slice of unstructured.Unstructured objects. func ReadPluginDeps(rootDir, bsName, bsNamespace string, enabledDirs []string) ([]*unstructured.Unstructured, error) { @@ -24,7 +22,7 @@ func ReadPluginDeps(rootDir, bsName, bsNamespace string, enabledDirs []string) ( var objects []*unstructured.Unstructured // Read the directory tree - files, err := processDepsTree(rootDir, enabledDirs) + files, err := getDepsFiles(rootDir, enabledDirs) if err != nil { return nil, err @@ -36,7 +34,7 @@ func ReadPluginDeps(rootDir, bsName, bsNamespace string, enabledDirs []string) ( } // Read file content - content, err := os.ReadFile(file) + content, err := os.ReadFile(filepath.Clean(file)) if err != nil { return nil, fmt.Errorf("failed to read file %s: %w", file, err) } @@ -57,54 +55,26 @@ func ReadPluginDeps(rootDir, bsName, bsNamespace string, enabledDirs []string) ( return objects, nil } -func processDepsTree(root string, enabledDirs []string) ([]string, error) { - // Normalize and store allowed directories - enabledMap := make(map[string]bool) - for _, dir := range enabledDirs { - enabledMap[filepath.Join(root, dir)] = true - } - files := []string{} +func getDepsFiles(root string, enabledDirs []string) ([]string, error) { + var files []string - // Traverse the directory tree - err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { + // Iterate over the specified directories + for _, dir := range enabledDirs { + dirPath := filepath.Join(root, dir) + // Read the directory contents + entries, err := os.ReadDir(dirPath) if err != nil { - return err + return nil, fmt.Errorf("failed to read directory %s: %w", dirPath, err) } - // Always allow traversal of directories - if d.IsDir() { - fmt.Println("Traversing directory:", path) - return nil + // Collect only files from the first level + for _, entry := range entries { + if !entry.IsDir() { // Skip subdirectories + files = append(files, filepath.Join(dirPath, entry.Name())) + } } - - // Only process files if the directory (or its parent) is allowed - if isEnabled(filepath.Dir(path), enabledMap) { - fmt.Println("Reading file:", path) - files = append(files, path) - } else { - fmt.Println("Skipping file:", path) - } - - return nil - }) - if err != nil { - return nil, err } - return files, nil -} -func isEnabled(path string, enabledMap map[string]bool) bool { - // Check if the path or any of its parent directories is in the allowed list - for { - if enabledMap[path] { - return true - } - parent := filepath.Dir(path) - if parent == path || parent == "." || parent == "/" { // Reached the root - break - } - path = parent - } - return false + return files, nil } diff --git a/pkg/model/plugin_deps_test.go b/pkg/model/plugin_deps_test.go index e9b72698d..0c5f5cae7 100644 --- a/pkg/model/plugin_deps_test.go +++ b/pkg/model/plugin_deps_test.go @@ -13,6 +13,13 @@ func TestReadPluginDeps(t *testing.T) { dir := t.TempDir() // Create subdirectories and files + // subdir1/ + // file0.yaml + // file1.yaml + // subdir11/ - second dir level to always ignore + // file11.yaml + // subdir2/ + // file2.yaml subdir1 := filepath.Join(dir, "subdir1") subdir11 := filepath.Join(subdir1, "subdir11") subdir2 := filepath.Join(dir, "subdir2") @@ -23,29 +30,29 @@ func TestReadPluginDeps(t *testing.T) { err = os.MkdirAll(subdir11, 0755) assert.NoError(t, err) - file0 := filepath.Join(dir, "file0.yaml") + file0 := filepath.Join(subdir1, "file0.yaml") file1 := filepath.Join(subdir1, "file1.yaml") + file11 := filepath.Join(subdir11, "file11.yaml") file2 := filepath.Join(subdir2, "file2.yaml") - file11 := filepath.Join(subdir11, "file2.yaml") err = os.WriteFile(file1, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test1"), 0644) assert.NoError(t, err) err = os.WriteFile(file2, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test2"), 0644) assert.NoError(t, err) err = os.WriteFile(file11, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test11"), 0644) assert.NoError(t, err) - err = os.WriteFile(file0, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test11"), 0644) + err = os.WriteFile(file0, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test0"), 0644) assert.NoError(t, err) - // Call ReadPluginDeps + // Call ReadPluginDeps for subdir1 objects, err := ReadPluginDeps(dir, "", "", []string{"subdir1"}) assert.NoError(t, err) assert.Len(t, objects, 2) - assert.Equal(t, "test1", objects[0].GetName()) - assert.Equal(t, "test11", objects[1].GetName()) + assert.Equal(t, "test0", objects[0].GetName()) + assert.Equal(t, "test1", objects[1].GetName()) objects, err = ReadPluginDeps(dir, "", "", []string{""}) assert.NoError(t, err) - assert.Len(t, objects, 4) + assert.Len(t, objects, 0) } func TestReadPluginDepsSubstitutions(t *testing.T) { From 22aff07f551e470050c8b9ba78db8001d50faffb Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Wed, 16 Apr 2025 16:58:23 +0300 Subject: [PATCH 05/35] fix make run Signed-off-by: gazarenkov --- Makefile | 2 +- ...kstage-operator.clusterserviceversion.yaml | 9 ++- .../rhdh-default-config_v1_configmap.yaml | 69 ++++++++++++++----- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index f1b8a2954..d19adfe87 100644 --- a/Makefile +++ b/Makefile @@ -221,7 +221,7 @@ build: manifests generate fmt vet ## Build manager binary. .PHONY: run run: manifests generate fmt vet $(LOCALBIN) ## Run a controller from your host. mkdir -p $(LOCALBIN)/default-config/ && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/operator/default-config/* $(LOCALBIN)/default-config/ - mkdir -p $(LOCALBIN)/plugin-deps/ && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps/ + mkdir -p $(LOCALBIN)/plugin-deps/ && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps/ 2>/dev/null || : go run -C $(LOCALBIN) ../cmd/main.go # by default images expire from quay registry after 14 days diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index 8be834e24..2746e95c2 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.7 - createdAt: "2025-04-01T13:01:17Z" + createdAt: "2025-04-16T13:52:03Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed @@ -266,7 +266,7 @@ spec: value: quay.io/fedora/postgresql-15:latest - name: RELATED_IMAGE_backstage value: quay.io/rhdh/rhdh-hub-rhel9:next - image: quay.io/rhdh/rhdh-rhel9-operator:1.7 + image: quay.io/gazarenk/backstage-operator:latest livenessProbe: httpGet: path: /healthz @@ -300,6 +300,8 @@ spec: - ALL readOnlyRootFilesystem: true volumeMounts: + - mountPath: plugin-deps + name: plugin-deps - mountPath: /default-config name: default-config securityContext: @@ -307,6 +309,9 @@ spec: serviceAccountName: rhdh-controller-manager terminationGracePeriodSeconds: 10 volumes: + - configMap: + name: rhdh-plugin-deps + name: plugin-deps - configMap: name: rhdh-default-config name: default-config diff --git a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml index 0a720c066..9161cad8c 100644 --- a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml @@ -348,28 +348,65 @@ data: securityContext: # any group id fsGroup: 1001 - dynamic-plugins.yaml: |- + dynamic-plugins.yaml: | + #apiVersion: v1 + #kind: ConfigMap + #metadata: + # name: default-dynamic-plugins # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name + #data: + # "dynamic-plugins.yaml": | + # ########################################################################################################### + # # /!\ WARNING + # # + # # This is the default dynamic plugins configuration file created and managed by the Operator for your CR. + # # Do NOT edit this manually in the Cluster, as your changes will be overridden by the Operator upon the + # # next reconciliation. + # # If you want to customize the dynamic plugins, you should create your own dynamic-plugins ConfigMap + # # and reference it in your CR. + # # See https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.4/html/installing_and_viewing_plugins_in_red_hat_developer_hub/rhdh-installing-rhdh-plugins_title-plugins-rhdh-about#proc-config-dynamic-plugins-rhdh-operator_rhdh-installing-rhdh-plugins + # # for more details or https://github.com/redhat-developer/rhdh-operator/blob/main/examples/rhdh-cr.yaml + # # for an example. + # ########################################################################################################### + # includes: + # - dynamic-plugins.default.yaml + # plugins: [] + #--- apiVersion: v1 kind: ConfigMap metadata: - name: default-dynamic-plugins # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name + name: default-dynamic-plugins data: - "dynamic-plugins.yaml": | - ########################################################################################################### - # /!\ WARNING - # - # This is the default dynamic plugins configuration file created and managed by the Operator for your CR. - # Do NOT edit this manually in the Cluster, as your changes will be overridden by the Operator upon the - # next reconciliation. - # If you want to customize the dynamic plugins, you should create your own dynamic-plugins ConfigMap - # and reference it in your CR. - # See https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.4/html/installing_and_viewing_plugins_in_red_hat_developer_hub/rhdh-installing-rhdh-plugins_title-plugins-rhdh-about#proc-config-dynamic-plugins-rhdh-operator_rhdh-installing-rhdh-plugins - # for more details or https://github.com/redhat-developer/rhdh-operator/blob/main/examples/rhdh-cr.yaml - # for an example. - ########################################################################################################### + plugin-dependencies: | + # orchestrator + dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml - plugins: [] + plugins: + - disabled: true #false + integrity: sha512-2aOHDLFrGMAtyHFiyGZwVBZ9Op+TmKYUwfZxwoaGJ1s6JSy/0qgqineEEE0K3dn/f17XBUj+H1dwa5Al598Ugw== + package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-backend-dynamic-1.4.0.tgz + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service + - disabled: true #false + integrity: sha512-2yasbfBZ3iKntArIfK+hk9tvv4b/dy9+WKXOcWIotqkI1gv+Nhvy+m55KAUWi2vmfM0rj3EoG6YP+3Zajn1KyA== + package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-1.4.0.tgz + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + module: OrchestratorPlugin + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + module: OrchestratorPlugin + path: /orchestrator route.yaml: |- apiVersion: route.openshift.io/v1 kind: Route From 593c4162e4a12a5e3da40c39f14276bffc2ba6c9 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Mon, 21 Apr 2025 15:57:55 +0300 Subject: [PATCH 06/35] fixes, sonataflow version Signed-off-by: gazarenkov --- Makefile | 2 +- .../operator/default-config/deployment.yaml | 2 + .../default-config/dynamic-plugins.yaml | 49 +++++++----- .../profile/rhdh/operator/kustomization.yaml | 2 +- .../plugin-deps/orchestrator/sonataflow.yaml | 18 ++--- .../orchestrator/rbac-sonataflow.yaml | 18 ++++- integration_tests/suite_test.go | 6 ++ pkg/utils/yaml_test.go | 80 +++++++++++++++++++ 8 files changed, 142 insertions(+), 35 deletions(-) create mode 100644 pkg/utils/yaml_test.go diff --git a/Makefile b/Makefile index d19adfe87..92e344b29 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ PROFILE_SHORT := $(shell echo $(PROFILE) | cut -d. -f1) # VERSION defines the project version for the bundle. # Update this value when you upgrade the version of your project. -# To re-generate a bundle for another specific version without changing the st:andard setup, you can: +# To re-generate a bundle for another specific version without changing the standard setup, you can: # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) # Set a default VERSION if it is not defined diff --git a/config/profile/rhdh/operator/default-config/deployment.yaml b/config/profile/rhdh/operator/default-config/deployment.yaml index e726cd663..7d86e11c3 100644 --- a/config/profile/rhdh/operator/default-config/deployment.yaml +++ b/config/profile/rhdh/operator/default-config/deployment.yaml @@ -62,6 +62,8 @@ spec: env: - name: NPM_CONFIG_USERCONFIG value: /opt/app-root/src/.npmrc.dynamic-plugins + - name: MAX_ENTRY_SIZE + value: 30000000 volumeMounts: - mountPath: /dynamic-plugins-root name: dynamic-plugins-root diff --git a/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml b/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml index 69dfe701c..6d268cef5 100644 --- a/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml +++ b/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml @@ -26,33 +26,40 @@ metadata: name: default-dynamic-plugins data: plugin-dependencies: | - # orchestrator +# orchestrator dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml plugins: - - disabled: true #false - integrity: sha512-2aOHDLFrGMAtyHFiyGZwVBZ9Op+TmKYUwfZxwoaGJ1s6JSy/0qgqineEEE0K3dn/f17XBUj+H1dwa5Al598Ugw== - package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-backend-dynamic-1.4.0.tgz + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz" + integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + module: OrchestratorPlugin + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + module: OrchestratorPlugin + path: /orchestrator + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-backend-dynamic-1.5.1.tgz" + integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g== pluginConfig: orchestrator: dataIndexService: url: http://sonataflow-platform-data-index-service - - disabled: true #false - integrity: sha512-2yasbfBZ3iKntArIfK+hk9tvv4b/dy9+WKXOcWIotqkI1gv+Nhvy+m55KAUWi2vmfM0rj3EoG6YP+3Zajn1KyA== - package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-1.4.0.tgz + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" + integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== pluginConfig: - dynamicPlugins: - frontend: - red-hat-developer-hub.backstage-plugin-orchestrator: - appIcons: - - importName: OrchestratorIcon - module: OrchestratorPlugin - name: orchestratorIcon - dynamicRoutes: - - importName: OrchestratorPage - menuItem: - icon: orchestratorIcon - text: Orchestrator - module: OrchestratorPlugin - path: /orchestrator + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service diff --git a/config/profile/rhdh/operator/kustomization.yaml b/config/profile/rhdh/operator/kustomization.yaml index f6148285a..916109806 100644 --- a/config/profile/rhdh/operator/kustomization.yaml +++ b/config/profile/rhdh/operator/kustomization.yaml @@ -43,4 +43,4 @@ configMapGenerator: - files: - plugin-deps/orchestrator/sonataflow.yaml - plugin-deps/orchestrator/sample-greetings.yaml - name: plugin-deps + name: plugin-deps-orchestrator diff --git a/config/profile/rhdh/operator/plugin-deps/orchestrator/sonataflow.yaml b/config/profile/rhdh/operator/plugin-deps/orchestrator/sonataflow.yaml index 090cba636..d7fd07c3b 100644 --- a/config/profile/rhdh/operator/plugin-deps/orchestrator/sonataflow.yaml +++ b/config/profile/rhdh/operator/plugin-deps/orchestrator/sonataflow.yaml @@ -84,7 +84,7 @@ spec: apiVersion: sonataflow.org/v1alpha08 kind: SonataFlowPlatform metadata: - name: sonataflow-platform # hardcoded + name: sonataflow-platform spec: monitoring: enabled: true @@ -103,22 +103,22 @@ spec: persistence: postgresql: secretRef: - name: backstage-psql-secret-{{backstage-name}} # hardcoded backstage-psql-secret-{{cr-name}} + name: backstage-psql-secret-{{backstage-name}} userKey: POSTGRES_USER passwordKey: POSTGRES_PASSWORD serviceRef: - name: backstage-psql-{{backstage-name}} # hardcoded backstage-psql-{{cr-name}} - namespace: {{backstage-ns}} # hardcoded {{cr-namespace}} - databaseName: backstage_plugin_orchestrator #hardcoded + name: backstage-psql-{{backstage-name}} + namespace: {{backstage-ns}} + databaseName: backstage_plugin_orchestrator jobService: enabled: true persistence: postgresql: secretRef: - name: backstage-psql-secret-{{backstage-name}} # hardcoded backstage-psql-secret-{{cr-name}} + name: backstage-psql-secret-{{backstage-name}} userKey: POSTGRES_USER passwordKey: POSTGRES_PASSWORD serviceRef: - name: backstage-psql-{{backstage-name}} # hardcoded backstage-psql-{{cr-name}} - namespace: {{backstage-ns}} # hardcoded {{cr-namespace}} - databaseName: backstage_plugin_orchestrator # hardcoded name of DB created by orchestrator plugin + name: backstage-psql-{{backstage-name}} + namespace: {{backstage-ns}} + databaseName: backstage_plugin_orchestrator diff --git a/config/profile/rhdh/plugin-infra/orchestrator/rbac-sonataflow.yaml b/config/profile/rhdh/plugin-infra/orchestrator/rbac-sonataflow.yaml index 92be1d5ec..997af3e8c 100644 --- a/config/profile/rhdh/plugin-infra/orchestrator/rbac-sonataflow.yaml +++ b/config/profile/rhdh/plugin-infra/orchestrator/rbac-sonataflow.yaml @@ -1,7 +1,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: manager-sonataflow-role + name: rhdh-manager-sonataflow-role rules: - apiGroups: - "sonataflow.org" @@ -28,18 +28,30 @@ rules: - patch - update - watch + - apiGroups: + - "operator.knative.dev" + resources: + - knativeeventings + - knativeservings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: backstage-operator app.kubernetes.io/instance: manager-rolebinding app.kubernetes.io/managed-by: kustomize app.kubernetes.io/name: clusterrolebinding app.kubernetes.io/part-of: backstage-operator - name: manager-sonataflow-rolebinding + name: rhdh-manager-sonataflow-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole diff --git a/integration_tests/suite_test.go b/integration_tests/suite_test.go index db3c03766..38981625f 100644 --- a/integration_tests/suite_test.go +++ b/integration_tests/suite_test.go @@ -89,6 +89,12 @@ var _ = BeforeSuite(func() { testEnv.UseExistingCluster = ptr.To(boolValue) } } + if !*testEnv.UseExistingCluster { + // force k8s platform if not use existing cluster + // this is needed for instance for the case when we run tests + // on test env but OCP cluster is configured as current + os.Setenv(utils.PlatformEnvVar, "k8s") + } if val, ok := os.LookupEnv("USE_EXISTING_CONTROLLER"); ok { boolValue, err := strconv.ParseBool(val) diff --git a/pkg/utils/yaml_test.go b/pkg/utils/yaml_test.go new file mode 100644 index 000000000..905604c5f --- /dev/null +++ b/pkg/utils/yaml_test.go @@ -0,0 +1,80 @@ +package utils + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadYamlFile(t *testing.T) { + // Create a temporary directory + dir, err := os.MkdirTemp("", "test-yaml") + assert.NoError(t, err) + defer os.RemoveAll(dir) + + // Create a sample YAML file + yamlContent := ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-configmap +data: + key: value +` + filePath := filepath.Join(dir, "test.yaml") + err = os.WriteFile(filePath, []byte(yamlContent), 0644) + assert.NoError(t, err) + + // Test ReadYamlFile + objects, err := ReadYamlFile(filePath) + assert.NoError(t, err) + assert.Len(t, objects, 1) + + obj := objects[0] + assert.Equal(t, "ConfigMap", obj.GetKind()) + assert.Equal(t, "test-configmap", obj.GetName()) +} + +func TestReadYamlFilesFromDir(t *testing.T) { + // Create a temporary directory + dir, err := os.MkdirTemp("", "test-yaml-dir") + assert.NoError(t, err) + defer os.RemoveAll(dir) + + // Create sample YAML files + yamlContent1 := ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-configmap1 +data: + key: value1 +` + yamlContent2 := ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-configmap2 +data: + key: value2 +` + err = os.WriteFile(filepath.Join(dir, "test1.yaml"), []byte(yamlContent1), 0644) + assert.NoError(t, err) + err = os.WriteFile(filepath.Join(dir, "test2.yaml"), []byte(yamlContent2), 0644) + assert.NoError(t, err) + + // Test ReadYamlFilesFromDir + objects, err := ReadYamlFilesFromDir(dir) + assert.NoError(t, err) + assert.Len(t, objects, 2) + + obj1 := objects[0] + assert.Equal(t, "ConfigMap", obj1.GetKind()) + assert.Equal(t, "test-configmap1", obj1.GetName()) + + obj2 := objects[1] + assert.Equal(t, "ConfigMap", obj2.GetKind()) + assert.Equal(t, "test-configmap2", obj2.GetName()) +} From 8556918b36b950941b40e2016a51bf1c5e8d6546 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Mon, 21 Apr 2025 15:59:14 +0300 Subject: [PATCH 07/35] fixes, sonataflow version Signed-off-by: gazarenkov --- config/profile/rhdh/operator/default-config/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/profile/rhdh/operator/default-config/deployment.yaml b/config/profile/rhdh/operator/default-config/deployment.yaml index 7d86e11c3..fa21dcfca 100644 --- a/config/profile/rhdh/operator/default-config/deployment.yaml +++ b/config/profile/rhdh/operator/default-config/deployment.yaml @@ -63,7 +63,7 @@ spec: - name: NPM_CONFIG_USERCONFIG value: /opt/app-root/src/.npmrc.dynamic-plugins - name: MAX_ENTRY_SIZE - value: 30000000 + value: "30000000" volumeMounts: - mountPath: /dynamic-plugins-root name: dynamic-plugins-root From 29abd82c9e7537bdea01067845dad0cf295be9ca Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Wed, 23 Apr 2025 14:42:19 +0300 Subject: [PATCH 08/35] add platform Signed-off-by: gazarenkov --- .../operator/patches/deployment-patch.yaml | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 config/profile/rhdh/operator/patches/deployment-patch.yaml diff --git a/config/profile/rhdh/operator/patches/deployment-patch.yaml b/config/profile/rhdh/operator/patches/deployment-patch.yaml new file mode 100644 index 000000000..b737681d6 --- /dev/null +++ b/config/profile/rhdh/operator/patches/deployment-patch.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: operator + labels: + app: rhdh-operator +spec: + selector: + matchLabels: + app: rhdh-operator + template: + metadata: + labels: + app: rhdh-operator + app.kubernetes.io/component: rhdh-operator + spec: + containers: + - name: manager + env: + - name: OPERATOR_NAME + value: rhdh-operator + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: RELATED_IMAGE_postgresql + value: quay.io/fedora/postgresql-15:latest + - name: RELATED_IMAGE_backstage + value: quay.io/rhdh/rhdh-hub-rhel9:next + volumeMounts: + - name: plugin-deps-orchestrator + mountPath: plugin-deps/orchestrator + volumes: + - name: plugin-deps-orchestrator + configMap: + name: rhdh-plugin-deps-orchestrator From c662f90dabf1307b13e7a29eec0a677cc2523027 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 23 Apr 2025 11:45:53 +0000 Subject: [PATCH 09/35] Regenerate bundle/installer manifests Co-authored-by: gazarenkov --- api/v1alpha1/zz_generated.deepcopy.go | 2 +- api/v1alpha2/zz_generated.deepcopy.go | 2 +- api/v1alpha3/zz_generated.deepcopy.go | 2 +- ...kstage-operator.clusterserviceversion.yaml | 2 +- ...kstage-operator.clusterserviceversion.yaml | 10 +- .../rhdh-default-config_v1_configmap.yaml | 92 ++--- ...plugin-deps-orchestrator_v1_configmap.yaml | 261 ++++++++++++++ .../profile/backstage.io/kustomization.yaml | 5 +- config/profile/rhdh/kustomization.yaml | 5 +- dist/rhdh/install.yaml | 325 ++++++++++++++++-- 10 files changed, 613 insertions(+), 93 deletions(-) create mode 100644 bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ca4d7a346..9b7acdd38 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha1 import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index 1357c08bd..c3ecd6b25 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha2 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index 6613a8896..b315b789e 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha3 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index 7c4391318..20c88cc12 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -35,7 +35,7 @@ metadata: } } ] - createdAt: "2025-04-01T13:01:14Z" + createdAt: "2025-04-23T11:45:47Z" description: Backstage Operator operators.operatorframework.io/builder: operator-sdk-v1.37.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index 2746e95c2..723983ac4 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.7 - createdAt: "2025-04-16T13:52:03Z" + createdAt: "2025-04-23T11:45:49Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed @@ -300,8 +300,8 @@ spec: - ALL readOnlyRootFilesystem: true volumeMounts: - - mountPath: plugin-deps - name: plugin-deps + - mountPath: plugin-deps/orchestrator + name: plugin-deps-orchestrator - mountPath: /default-config name: default-config securityContext: @@ -310,8 +310,8 @@ spec: terminationGracePeriodSeconds: 10 volumes: - configMap: - name: rhdh-plugin-deps - name: plugin-deps + name: rhdh-plugin-deps-orchestrator + name: plugin-deps-orchestrator - configMap: name: rhdh-default-config name: default-config diff --git a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml index 9161cad8c..d1d1456b0 100644 --- a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml @@ -238,6 +238,8 @@ data: env: - name: NPM_CONFIG_USERCONFIG value: /opt/app-root/src/.npmrc.dynamic-plugins + - name: MAX_ENTRY_SIZE + value: "30000000" volumeMounts: - mountPath: /dynamic-plugins-root name: dynamic-plugins-root @@ -348,65 +350,37 @@ data: securityContext: # any group id fsGroup: 1001 - dynamic-plugins.yaml: | - #apiVersion: v1 - #kind: ConfigMap - #metadata: - # name: default-dynamic-plugins # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name - #data: - # "dynamic-plugins.yaml": | - # ########################################################################################################### - # # /!\ WARNING - # # - # # This is the default dynamic plugins configuration file created and managed by the Operator for your CR. - # # Do NOT edit this manually in the Cluster, as your changes will be overridden by the Operator upon the - # # next reconciliation. - # # If you want to customize the dynamic plugins, you should create your own dynamic-plugins ConfigMap - # # and reference it in your CR. - # # See https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.4/html/installing_and_viewing_plugins_in_red_hat_developer_hub/rhdh-installing-rhdh-plugins_title-plugins-rhdh-about#proc-config-dynamic-plugins-rhdh-operator_rhdh-installing-rhdh-plugins - # # for more details or https://github.com/redhat-developer/rhdh-operator/blob/main/examples/rhdh-cr.yaml - # # for an example. - # ########################################################################################################### - # includes: - # - dynamic-plugins.default.yaml - # plugins: [] - #--- - apiVersion: v1 - kind: ConfigMap - metadata: - name: default-dynamic-plugins - data: - plugin-dependencies: | - # orchestrator - dynamic-plugins.yaml: | - includes: - - dynamic-plugins.default.yaml - plugins: - - disabled: true #false - integrity: sha512-2aOHDLFrGMAtyHFiyGZwVBZ9Op+TmKYUwfZxwoaGJ1s6JSy/0qgqineEEE0K3dn/f17XBUj+H1dwa5Al598Ugw== - package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-backend-dynamic-1.4.0.tgz - pluginConfig: - orchestrator: - dataIndexService: - url: http://sonataflow-platform-data-index-service - - disabled: true #false - integrity: sha512-2yasbfBZ3iKntArIfK+hk9tvv4b/dy9+WKXOcWIotqkI1gv+Nhvy+m55KAUWi2vmfM0rj3EoG6YP+3Zajn1KyA== - package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-1.4.0.tgz - pluginConfig: - dynamicPlugins: - frontend: - red-hat-developer-hub.backstage-plugin-orchestrator: - appIcons: - - importName: OrchestratorIcon - module: OrchestratorPlugin - name: orchestratorIcon - dynamicRoutes: - - importName: OrchestratorPage - menuItem: - icon: orchestratorIcon - text: Orchestrator - module: OrchestratorPlugin - path: /orchestrator + dynamic-plugins.yaml: "#apiVersion: v1\n#kind: ConfigMap\n#metadata:\n# name: default-dynamic-plugins + # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name\n#data:\n# + \ \"dynamic-plugins.yaml\": |\n# ###########################################################################################################\n# + \ # /!\\ WARNING\n# #\n# # This is the default dynamic plugins configuration + file created and managed by the Operator for your CR.\n# # Do NOT edit this + manually in the Cluster, as your changes will be overridden by the Operator upon + the\n# # next reconciliation.\n# # If you want to customize the dynamic + plugins, you should create your own dynamic-plugins ConfigMap\n# # and reference + it in your CR.\n# # See https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.4/html/installing_and_viewing_plugins_in_red_hat_developer_hub/rhdh-installing-rhdh-plugins_title-plugins-rhdh-about#proc-config-dynamic-plugins-rhdh-operator_rhdh-installing-rhdh-plugins\n# + \ # for more details or https://github.com/redhat-developer/rhdh-operator/blob/main/examples/rhdh-cr.yaml\n# + \ # for an example.\n# ###########################################################################################################\n# + \ includes:\n# - dynamic-plugins.default.yaml\n# plugins: []\n#---\napiVersion: + v1\nkind: ConfigMap\nmetadata:\n name: default-dynamic-plugins\ndata:\n plugin-dependencies: + |\n# orchestrator\n dynamic-plugins.yaml: |\n includes:\n - dynamic-plugins.default.yaml\n + \ plugins:\n - disabled: false\n package: \"https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz\"\n + \ integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== + \ \n pluginConfig:\n dynamicPlugins:\n frontend:\n + \ red-hat-developer-hub.backstage-plugin-orchestrator:\n appIcons:\n + \ - importName: OrchestratorIcon\n module: + OrchestratorPlugin\n name: orchestratorIcon\n dynamicRoutes:\n + \ - importName: OrchestratorPage\n menuItem:\n + \ icon: orchestratorIcon\n text: + Orchestrator\n module: OrchestratorPlugin\n path: + /orchestrator\n - disabled: false\n package: \"https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-backend-dynamic-1.5.1.tgz\"\n + \ integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g==\n + \ pluginConfig:\n orchestrator:\n dataIndexService:\n + \ url: http://sonataflow-platform-data-index-service\n - disabled: + false\n package: \"https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz\"\n + \ integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ==\n + \ pluginConfig:\n orchestrator:\n dataIndexService:\n + \ url: http://sonataflow-platform-data-index-service\n" route.yaml: |- apiVersion: route.openshift.io/v1 kind: Route diff --git a/bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml new file mode 100644 index 000000000..1ba94f0c5 --- /dev/null +++ b/bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml @@ -0,0 +1,261 @@ +apiVersion: v1 +data: + sample-greetings.yaml: |+ + apiVersion: v1 + data: + greeting.sw.input-schema.json: | + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "language": { + "title": "Language", + "description": "Language to greet", + "type": "string", + "enum": ["English", "Spanish"], + "default": "English" + } + } + } + workflow-output-schema.json: |- + { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "WorkflowResult", + "description": "Schema of workflow output", + "type": "object", + "properties": { + "result": { + "$ref": "../shared/schemas/workflow-result-schema.json", + "type": "object" + } + } + } + kind: ConfigMap + metadata: + name: 01-greeting-resources-schemas + --- + apiVersion: sonataflow.org/v1alpha08 + kind: SonataFlow + metadata: + annotations: + sonataflow.org/description: YAML based greeting workflow + sonataflow.org/expressionLang: jq + sonataflow.org/profile: gitops + sonataflow.org/version: "1.0" + labels: + app: greeting + sonataflow.org/workflow-app: greeting + name: greeting + spec: + flow: + annotations: + - workflow-type/infrastructure + dataInputSchema: + failOnValidationErrors: true + schema: schemas/greeting.sw.input-schema.json + functions: + - name: greetFunction + operation: sysout + type: custom + - name: successResult + operation: '{ "result": { "message": "Greeting workflow completed successfully", "outputs":[ { "key":"Selected language", "value": .language }, { "key":"Greeting message", "value": .greeting } ] } }' + type: expression + start: + stateName: ChooseOnLanguage + states: + - dataConditions: + - condition: .language == "English" + transition: + nextState: GreetInEnglish + - condition: .language == "Spanish" + transition: + nextState: GreetInSpanish + defaultCondition: + transition: + nextState: GreetInEnglish + name: ChooseOnLanguage + type: switch + - data: + greeting: Hello from YAML Workflow + name: GreetInEnglish + transition: + nextState: GreetPerson + type: inject + - data: + greeting: Saludos desde YAML Workflow + name: GreetInSpanish + transition: + nextState: GreetPerson + type: inject + - actionMode: sequential + actions: + - actionDataFilter: + useResults: true + functionRef: + arguments: + message: .greeting + invoke: sync + refName: greetFunction + name: greetAction + - actionDataFilter: + useResults: true + functionRef: + invoke: sync + refName: successResult + name: setOutput + end: + terminate: true + name: GreetPerson + type: operation + podTemplate: + container: + resources: {} + image: quay.io/orchestrator/serverless-workflow-greeting:96d772297d3ee964ab9775d7aae7dbbd4f070dd7 + resources: + configMaps: + - configMap: + name: 01-greeting-resources-schemas + workflowPath: schemas + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} + userKey: POSTGRES_USER #hardcoded + passwordKey: POSTGRES_PASSWORD #hardcoded + serviceRef: + name: backstage-psql-{{backstage-name}} # hardcoded backstage-psql-{{cr-name}} + namespace: {{backstage-ns}} # hardcoded {{cr-namespace}} + databaseName: backstage_plugin_orchestrator #hardcoded + port: 5432 + databaseSchema: greeting + status: + address: {} + + sonataflow.yaml: | + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-knative-to-sonataflow-and-workflows # hardcoded + spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow knative events to be delivered to workflows. + kubernetes.io/metadata.name: knative-eventing + - namespaceSelector: + matchLabels: + # Allow auxiliary knative function for workflow (such as m2k-save-transformation) + kubernetes.io/metadata.name: knative-serving + --- + # NetworkPolicy to unblock incoming traffic to the namespace + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-external-communication # hardcoded + spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow knative events to be delivered to workflows. + kubernetes.io/metadata.name: openshift-ingress + --- + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-intra-network # hardcoded + spec: + # Apply this policy to all pods in the namespace + podSelector: {} + # Specify policy type as 'Ingress' to control incoming traffic rules + policyTypes: + - Ingress + ingress: + - from: + # Allow ingress from any pod within the same namespace + - podSelector: {} + --- + # NetworkPolicy to allow openshift-user-workload-monitoring pods to access all pods within the workflow's namespace + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-monitoring-to-sonataflow-and-workflows # hardcoded + spec: + # Apply this policy to all pods in the namespace + podSelector: {} + # Specify policy type as 'Ingress' to control incoming traffic rules + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow openshift-user-workload-monitoring pods to access the workflow. + kubernetes.io/metadata.name: openshift-user-workload-monitoring + --- + apiVersion: operator.knative.dev/v1beta1 + kind: KnativeEventing + metadata: + name: knative-eventing + namespace: knative-eventing + spec: + Registry: {} + --- + apiVersion: operator.knative.dev/v1beta1 + kind: KnativeServing + metadata: + name: knative-serving + namespace: knative-serving + spec: + controller-custom-certs: + name: "" + type: "" + registry: {} + --- + apiVersion: sonataflow.org/v1alpha08 + kind: SonataFlowPlatform + metadata: + name: sonataflow-platform + spec: + monitoring: + enabled: true + build: + template: + resources: + requests: + memory: 64Mi + cpu: 250m + limits: + memory: 1Gi + cpu: 500m + services: + dataIndex: + enabled: true + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: backstage-psql-{{backstage-name}} + namespace: {{backstage-ns}} + databaseName: backstage_plugin_orchestrator + jobService: + enabled: true + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: backstage-psql-{{backstage-name}} + namespace: {{backstage-ns}} + databaseName: backstage_plugin_orchestrator +kind: ConfigMap +metadata: + name: rhdh-plugin-deps-orchestrator diff --git a/config/profile/backstage.io/kustomization.yaml b/config/profile/backstage.io/kustomization.yaml index 3f9818e3d..cb55fb7e1 100644 --- a/config/profile/backstage.io/kustomization.yaml +++ b/config/profile/backstage.io/kustomization.yaml @@ -3,4 +3,7 @@ kind: Kustomization resources: - operator - +images: +- name: controller + newName: quay.io/rhdh-community/operator + newTag: 0.7.0 diff --git a/config/profile/rhdh/kustomization.yaml b/config/profile/rhdh/kustomization.yaml index 500207b31..67253cc6d 100644 --- a/config/profile/rhdh/kustomization.yaml +++ b/config/profile/rhdh/kustomization.yaml @@ -3,4 +3,7 @@ kind: Kustomization resources: - operator -#- plugin-infra +images: +- name: controller + newName: quay.io/rhdh/rhdh-rhel9-operator + newTag: "1.7" diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index a6a664463..689cd162f 100644 --- a/dist/rhdh/install.yaml +++ b/dist/rhdh/install.yaml @@ -1749,6 +1749,8 @@ data: env: - name: NPM_CONFIG_USERCONFIG value: /opt/app-root/src/.npmrc.dynamic-plugins + - name: MAX_ENTRY_SIZE + value: "30000000" volumeMounts: - mountPath: /dynamic-plugins-root name: dynamic-plugins-root @@ -1859,28 +1861,37 @@ data: securityContext: # any group id fsGroup: 1001 - dynamic-plugins.yaml: |- - apiVersion: v1 - kind: ConfigMap - metadata: - name: default-dynamic-plugins # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name - data: - "dynamic-plugins.yaml": | - ########################################################################################################### - # /!\ WARNING - # - # This is the default dynamic plugins configuration file created and managed by the Operator for your CR. - # Do NOT edit this manually in the Cluster, as your changes will be overridden by the Operator upon the - # next reconciliation. - # If you want to customize the dynamic plugins, you should create your own dynamic-plugins ConfigMap - # and reference it in your CR. - # See https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.4/html/installing_and_viewing_plugins_in_red_hat_developer_hub/rhdh-installing-rhdh-plugins_title-plugins-rhdh-about#proc-config-dynamic-plugins-rhdh-operator_rhdh-installing-rhdh-plugins - # for more details or https://github.com/redhat-developer/rhdh-operator/blob/main/examples/rhdh-cr.yaml - # for an example. - ########################################################################################################### - includes: - - dynamic-plugins.default.yaml - plugins: [] + dynamic-plugins.yaml: "#apiVersion: v1\n#kind: ConfigMap\n#metadata:\n# name: default-dynamic-plugins + # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name\n#data:\n# + \ \"dynamic-plugins.yaml\": |\n# ###########################################################################################################\n# + \ # /!\\ WARNING\n# #\n# # This is the default dynamic plugins configuration + file created and managed by the Operator for your CR.\n# # Do NOT edit this + manually in the Cluster, as your changes will be overridden by the Operator upon + the\n# # next reconciliation.\n# # If you want to customize the dynamic + plugins, you should create your own dynamic-plugins ConfigMap\n# # and reference + it in your CR.\n# # See https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.4/html/installing_and_viewing_plugins_in_red_hat_developer_hub/rhdh-installing-rhdh-plugins_title-plugins-rhdh-about#proc-config-dynamic-plugins-rhdh-operator_rhdh-installing-rhdh-plugins\n# + \ # for more details or https://github.com/redhat-developer/rhdh-operator/blob/main/examples/rhdh-cr.yaml\n# + \ # for an example.\n# ###########################################################################################################\n# + \ includes:\n# - dynamic-plugins.default.yaml\n# plugins: []\n#---\napiVersion: + v1\nkind: ConfigMap\nmetadata:\n name: default-dynamic-plugins\ndata:\n plugin-dependencies: + |\n# orchestrator\n dynamic-plugins.yaml: |\n includes:\n - dynamic-plugins.default.yaml\n + \ plugins:\n - disabled: false\n package: \"https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz\"\n + \ integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== + \ \n pluginConfig:\n dynamicPlugins:\n frontend:\n + \ red-hat-developer-hub.backstage-plugin-orchestrator:\n appIcons:\n + \ - importName: OrchestratorIcon\n module: + OrchestratorPlugin\n name: orchestratorIcon\n dynamicRoutes:\n + \ - importName: OrchestratorPage\n menuItem:\n + \ icon: orchestratorIcon\n text: + Orchestrator\n module: OrchestratorPlugin\n path: + /orchestrator\n - disabled: false\n package: \"https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-backend-dynamic-1.5.1.tgz\"\n + \ integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g==\n + \ pluginConfig:\n orchestrator:\n dataIndexService:\n + \ url: http://sonataflow-platform-data-index-service\n - disabled: + false\n package: \"https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz\"\n + \ integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ==\n + \ pluginConfig:\n orchestrator:\n dataIndexService:\n + \ url: http://sonataflow-platform-data-index-service\n" route.yaml: |- apiVersion: route.openshift.io/v1 kind: Route @@ -1931,6 +1942,269 @@ metadata: namespace: rhdh-operator --- apiVersion: v1 +data: + sample-greetings.yaml: |+ + apiVersion: v1 + data: + greeting.sw.input-schema.json: | + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "language": { + "title": "Language", + "description": "Language to greet", + "type": "string", + "enum": ["English", "Spanish"], + "default": "English" + } + } + } + workflow-output-schema.json: |- + { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "WorkflowResult", + "description": "Schema of workflow output", + "type": "object", + "properties": { + "result": { + "$ref": "../shared/schemas/workflow-result-schema.json", + "type": "object" + } + } + } + kind: ConfigMap + metadata: + name: 01-greeting-resources-schemas + --- + apiVersion: sonataflow.org/v1alpha08 + kind: SonataFlow + metadata: + annotations: + sonataflow.org/description: YAML based greeting workflow + sonataflow.org/expressionLang: jq + sonataflow.org/profile: gitops + sonataflow.org/version: "1.0" + labels: + app: greeting + sonataflow.org/workflow-app: greeting + name: greeting + spec: + flow: + annotations: + - workflow-type/infrastructure + dataInputSchema: + failOnValidationErrors: true + schema: schemas/greeting.sw.input-schema.json + functions: + - name: greetFunction + operation: sysout + type: custom + - name: successResult + operation: '{ "result": { "message": "Greeting workflow completed successfully", "outputs":[ { "key":"Selected language", "value": .language }, { "key":"Greeting message", "value": .greeting } ] } }' + type: expression + start: + stateName: ChooseOnLanguage + states: + - dataConditions: + - condition: .language == "English" + transition: + nextState: GreetInEnglish + - condition: .language == "Spanish" + transition: + nextState: GreetInSpanish + defaultCondition: + transition: + nextState: GreetInEnglish + name: ChooseOnLanguage + type: switch + - data: + greeting: Hello from YAML Workflow + name: GreetInEnglish + transition: + nextState: GreetPerson + type: inject + - data: + greeting: Saludos desde YAML Workflow + name: GreetInSpanish + transition: + nextState: GreetPerson + type: inject + - actionMode: sequential + actions: + - actionDataFilter: + useResults: true + functionRef: + arguments: + message: .greeting + invoke: sync + refName: greetFunction + name: greetAction + - actionDataFilter: + useResults: true + functionRef: + invoke: sync + refName: successResult + name: setOutput + end: + terminate: true + name: GreetPerson + type: operation + podTemplate: + container: + resources: {} + image: quay.io/orchestrator/serverless-workflow-greeting:96d772297d3ee964ab9775d7aae7dbbd4f070dd7 + resources: + configMaps: + - configMap: + name: 01-greeting-resources-schemas + workflowPath: schemas + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} + userKey: POSTGRES_USER #hardcoded + passwordKey: POSTGRES_PASSWORD #hardcoded + serviceRef: + name: backstage-psql-{{backstage-name}} # hardcoded backstage-psql-{{cr-name}} + namespace: {{backstage-ns}} # hardcoded {{cr-namespace}} + databaseName: backstage_plugin_orchestrator #hardcoded + port: 5432 + databaseSchema: greeting + status: + address: {} + + sonataflow.yaml: | + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-knative-to-sonataflow-and-workflows # hardcoded + spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow knative events to be delivered to workflows. + kubernetes.io/metadata.name: knative-eventing + - namespaceSelector: + matchLabels: + # Allow auxiliary knative function for workflow (such as m2k-save-transformation) + kubernetes.io/metadata.name: knative-serving + --- + # NetworkPolicy to unblock incoming traffic to the namespace + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-external-communication # hardcoded + spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow knative events to be delivered to workflows. + kubernetes.io/metadata.name: openshift-ingress + --- + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-intra-network # hardcoded + spec: + # Apply this policy to all pods in the namespace + podSelector: {} + # Specify policy type as 'Ingress' to control incoming traffic rules + policyTypes: + - Ingress + ingress: + - from: + # Allow ingress from any pod within the same namespace + - podSelector: {} + --- + # NetworkPolicy to allow openshift-user-workload-monitoring pods to access all pods within the workflow's namespace + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-monitoring-to-sonataflow-and-workflows # hardcoded + spec: + # Apply this policy to all pods in the namespace + podSelector: {} + # Specify policy type as 'Ingress' to control incoming traffic rules + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow openshift-user-workload-monitoring pods to access the workflow. + kubernetes.io/metadata.name: openshift-user-workload-monitoring + --- + apiVersion: operator.knative.dev/v1beta1 + kind: KnativeEventing + metadata: + name: knative-eventing + namespace: knative-eventing + spec: + Registry: {} + --- + apiVersion: operator.knative.dev/v1beta1 + kind: KnativeServing + metadata: + name: knative-serving + namespace: knative-serving + spec: + controller-custom-certs: + name: "" + type: "" + registry: {} + --- + apiVersion: sonataflow.org/v1alpha08 + kind: SonataFlowPlatform + metadata: + name: sonataflow-platform + spec: + monitoring: + enabled: true + build: + template: + resources: + requests: + memory: 64Mi + cpu: 250m + limits: + memory: 1Gi + cpu: 500m + services: + dataIndex: + enabled: true + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: backstage-psql-{{backstage-name}} + namespace: {{backstage-ns}} + databaseName: backstage_plugin_orchestrator + jobService: + enabled: true + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: backstage-psql-{{backstage-name}} + namespace: {{backstage-ns}} + databaseName: backstage_plugin_orchestrator +kind: ConfigMap +metadata: + name: rhdh-plugin-deps-orchestrator + namespace: rhdh-operator +--- +apiVersion: v1 kind: Service metadata: labels: @@ -2009,7 +2283,7 @@ spec: value: quay.io/fedora/postgresql-15:latest - name: RELATED_IMAGE_backstage value: quay.io/rhdh/rhdh-hub-rhel9:next - image: quay.io/rhdh/rhdh-rhel9-operator:1.7 + image: quay.io/gazarenk/backstage-operator:latest livenessProbe: httpGet: path: /healthz @@ -2043,6 +2317,8 @@ spec: - ALL readOnlyRootFilesystem: true volumeMounts: + - mountPath: plugin-deps/orchestrator + name: plugin-deps-orchestrator - mountPath: /default-config name: default-config securityContext: @@ -2050,6 +2326,9 @@ spec: serviceAccountName: rhdh-controller-manager terminationGracePeriodSeconds: 10 volumes: + - configMap: + name: rhdh-plugin-deps-orchestrator + name: plugin-deps-orchestrator - configMap: name: rhdh-default-config name: default-config From 08ea36d627faf2cb46ac45ed715318805a910a79 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Fri, 25 Apr 2025 10:33:38 +0300 Subject: [PATCH 10/35] plugin-infra target and dependencies on dynamic-plugins.yaml Signed-off-by: gazarenkov --- Makefile | 3 + .../default-config/dynamic-plugins.yaml | 4 +- internal/controller/plugin-deps.go | 5 +- pkg/model/dynamic-plugins.go | 76 +++++++++++++++--- pkg/model/dynamic-plugins_test.go | 77 +++++++++++++++++-- pkg/model/plugin_deps.go | 19 +++++ 6 files changed, 162 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 92e344b29..de3a68c70 100644 --- a/Makefile +++ b/Makefile @@ -364,6 +364,9 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. $(KUSTOMIZE) build config/profile/$(PROFILE) | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - +.PHONY: plugin-infra +plugin-infra: + $(KUSTOMIZE) build config/profile/$(PROFILE)/plugin-infra | $(KUBECTL) apply -f - ##@ OLM Deployment # It has to be the same namespace as ./config/default/kustomization.yaml -> namespace diff --git a/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml b/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml index 6d268cef5..b2ec785ce 100644 --- a/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml +++ b/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml @@ -25,7 +25,7 @@ kind: ConfigMap metadata: name: default-dynamic-plugins data: - plugin-dependencies: | +# plugin-dependencies: | # orchestrator dynamic-plugins.yaml: | includes: @@ -56,6 +56,8 @@ data: orchestrator: dataIndexService: url: http://sonataflow-platform-data-index-service + dependencies: + - ref: orchestrator - disabled: false package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== diff --git a/internal/controller/plugin-deps.go b/internal/controller/plugin-deps.go index c879a9133..fe0198f95 100644 --- a/internal/controller/plugin-deps.go +++ b/internal/controller/plugin-deps.go @@ -4,8 +4,6 @@ import ( "context" "errors" "fmt" - "os" - "path/filepath" "strings" "github.com/redhat-developer/rhdh-operator/pkg/model" @@ -20,9 +18,8 @@ import ( func (r *BackstageReconciler) applyPluginDeps(ctx context.Context, nsName types.NamespacedName, plugins model.DynamicPlugins) error { lg := log.FromContext(ctx) - dir := filepath.Join(os.Getenv("LOCALBIN"), "plugin-deps") - objects, err := model.ReadPluginDeps(dir, nsName.Name, nsName.Namespace, plugins.Dependencies()) + objects, err := model.GetPluginDeps(nsName.Name, nsName.Namespace, plugins) if err != nil { return fmt.Errorf("failed to read YAML files: %w", err) } diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index ba5fab476..5a2f2aab5 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -2,8 +2,8 @@ package model import ( "fmt" + "gopkg.in/yaml.v2" "os" - "strings" "golang.org/x/exp/maps" @@ -30,7 +30,6 @@ import ( const dynamicPluginInitContainerName = "install-dynamic-plugins" const DynamicPluginsFile = "dynamic-plugins.yaml" -const EnabledPluginsDepsFile = "plugin-dependencies" type DynamicPluginsFactory struct{} @@ -42,6 +41,23 @@ type DynamicPlugins struct { ConfigMap *corev1.ConfigMap } +type DynaPluginsConfig struct { + Includes []string `yaml:"includes"` + Plugins []DynaPlugin `yaml:"plugins"` +} + +type DynaPlugin struct { + Package string `yaml:"package"` + Integrity string `yaml:"integrity"` + Disabled bool `yaml:"disabled"` + PluginConfig map[string]interface{} `yaml:"pluginConfig"` + Dependencies []PluginDependency `yaml:"dependencies"` +} + +type PluginDependency struct { + Ref string `yaml:"ref"` +} + func init() { registerConfig("dynamic-plugins.yaml", DynamicPluginsFactory{}, false) } @@ -63,8 +79,8 @@ func addDynamicPluginsFromSpec(spec bsv1.BackstageSpec, model *BackstageModel) e dp := model.ExternalConfig.DynamicPlugins - if dp.Data == nil || (dp.Data[DynamicPluginsFile] == "" && dp.Data[EnabledPluginsDepsFile] == "") { - return fmt.Errorf("dynamic plugin configMap expects '%s' and|or '%s' Data keys", DynamicPluginsFile, EnabledPluginsDepsFile) + if dp.Data == nil || dp.Data[DynamicPluginsFile] == "" { + return fmt.Errorf("dynamic plugin configMap expects '%s' Data key", DynamicPluginsFile) } if dp.Data[DynamicPluginsFile] != "" { @@ -143,15 +159,55 @@ func (p *DynamicPlugins) setMetaInfo(backstage bsv1.Backstage, scheme *runtime.S setMetaInfo(p.ConfigMap, backstage, scheme) } -func (p *DynamicPlugins) Dependencies() []string { +func (p *DynamicPlugins) Dependencies() ([]PluginDependency, error) { + ps, err := p.pluginsFromConfigMap() + if err != nil { + return nil, err + } + + result := make([]PluginDependency, 0) + + for _, pp := range ps { + if pp.Disabled { + continue + } + + result = append(result, pp.Dependencies...) + } + + return result, nil + + //if p.ConfigMap == nil { + // return []string{} + //} + // + //data := p.ConfigMap.Data[EnabledPluginsDepsFile] + //if data != "" { + // return strings.Split(data, "\n") + //} + // + //return []string{} +} + +func (p *DynamicPlugins) pluginsFromConfigMap() ([]DynaPlugin, error) { if p.ConfigMap == nil { - return []string{} + return nil, fmt.Errorf("dynamic plugins configMap is not set") } - data := p.ConfigMap.Data[EnabledPluginsDepsFile] - if data != "" { - return strings.Split(data, "\n") + + data := p.ConfigMap.Data[DynamicPluginsFile] + if data == "" { + return nil, fmt.Errorf("dynamic plugins configMap does not contain %s key", DynamicPluginsFile) + } + + //var plugins []DynaPlugin + var pluginsConfig DynaPluginsConfig + err := yaml.Unmarshal([]byte(data), &pluginsConfig) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal dynamic plugins data: %w", err) } - return []string{} + + return pluginsConfig.Plugins, nil + } // returns initContainer supposed to initialize DynamicPlugins diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index 618f079dc..5d7f0c9ab 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "gopkg.in/yaml.v2" + "github.com/redhat-developer/rhdh-operator/pkg/utils" "k8s.io/utils/ptr" @@ -61,7 +63,7 @@ func TestDynamicPluginsInvalidKeyName(t *testing.T) { assert.Error(t, err) //assert.Contains(t, err.Error(), "expects exactly one Data key named 'dynamic-plugins.yaml'") - assert.Contains(t, err.Error(), "dynamic plugin configMap expects 'dynamic-plugins.yaml' and|or 'plugin-dependencies' Data keys") + assert.Contains(t, err.Error(), "dynamic plugin configMap expects 'dynamic-plugins.yaml' Data key") } @@ -92,7 +94,9 @@ func TestDefaultDynamicPlugins(t *testing.T) { //vol-default-dynamic-plugins assert.Equal(t, 4, len(ic.VolumeMounts)) - assert.Equal(t, 0, len(model.DynamicPlugins.Dependencies())) + deps, err := model.DynamicPlugins.Dependencies() + assert.NoError(t, err) + assert.Equal(t, 0, len(deps)) } @@ -107,7 +111,7 @@ func TestDefaultAndSpecifiedDynamicPlugins(t *testing.T) { testObj.externalConfig.DynamicPlugins = corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "dplugin"}, - Data: map[string]string{DynamicPluginsFile: "tt"}, + Data: map[string]string{DynamicPluginsFile: "dynamic-plugins.yaml: | \n plugins: []"}, } model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, false, testObj.scheme) @@ -124,7 +128,9 @@ func TestDefaultAndSpecifiedDynamicPlugins(t *testing.T) { assert.Equal(t, 4, len(ic.VolumeMounts)) assert.Equal(t, utils.GenerateVolumeNameFromCmOrSecret("dplugin"), ic.VolumeMounts[3].Name) - assert.Equal(t, 0, len(model.DynamicPlugins.Dependencies())) + deps, err := model.DynamicPlugins.Dependencies() + assert.NoError(t, err) + assert.Equal(t, 0, len(deps)) } func TestDynamicPluginsFailOnArbitraryDepl(t *testing.T) { @@ -166,17 +172,27 @@ func TestWithDynamicPluginsDeps(t *testing.T) { addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") + yamlData := `"dynamic-plugins.yaml": | +plugins: + - package: "plugin-a" + disabled: false + dependencies: + - ref: "dependency-1" + - ref: "dependency-2" +` + testObj.externalConfig.DynamicPlugins = corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: "dplugin"}, - Data: map[string]string{DynamicPluginsFile: "tt", EnabledPluginsDepsFile: `dep1 -dep2`}, + Data: map[string]string{DynamicPluginsFile: yamlData}, } model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, false, testObj.scheme) assert.NoError(t, err) assert.NotNil(t, model) - assert.Equal(t, 2, len(model.DynamicPlugins.Dependencies())) + deps, err := model.DynamicPlugins.Dependencies() + assert.NoError(t, err) + assert.Equal(t, 2, len(deps)) } @@ -188,3 +204,50 @@ func initContainer(model *BackstageModel) *corev1.Container { } return nil } + +func TestUnmarshalDynaPluginsConfig(t *testing.T) { + yamlData := ` +plugins: + - package: "plugin-a" + integrity: "sha256-abc123" + disabled: false + pluginConfig: + key1: "value1" + key2: "value2" + dependencies: + - ref: "dependency-1" + - ref: "dependency-2" + - package: "plugin-b" + integrity: "sha256-def456" + disabled: true + pluginConfig: + key3: "value3" + dependencies: [] +` + + var config DynaPluginsConfig + err := yaml.Unmarshal([]byte(yamlData), &config) + assert.NoError(t, err) + + // Validate plugins + assert.Equal(t, 2, len(config.Plugins)) + + // Validate first plugin + pluginA := config.Plugins[0] + assert.Equal(t, "plugin-a", pluginA.Package) + assert.Equal(t, "sha256-abc123", pluginA.Integrity) + assert.False(t, pluginA.Disabled) + assert.Equal(t, "value1", pluginA.PluginConfig["key1"]) + assert.Equal(t, "value2", pluginA.PluginConfig["key2"]) + assert.Equal(t, 2, len(pluginA.Dependencies)) + assert.Equal(t, "dependency-1", pluginA.Dependencies[0].Ref) + assert.Equal(t, "dependency-2", pluginA.Dependencies[1].Ref) + + // Validate second plugin + pluginB := config.Plugins[1] + assert.Equal(t, "plugin-b", pluginB.Package) + assert.Equal(t, "sha256-def456", pluginB.Integrity) + assert.True(t, pluginB.Disabled) + assert.Equal(t, "value3", pluginB.PluginConfig["key3"]) + assert.Empty(t, pluginB.Dependencies) +} diff --git a/pkg/model/plugin_deps.go b/pkg/model/plugin_deps.go index dd49f6f3e..04bdfba4e 100644 --- a/pkg/model/plugin_deps.go +++ b/pkg/model/plugin_deps.go @@ -11,6 +11,25 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +func GetPluginDeps(bsName, bsNamespace string, plugins DynamicPlugins) ([]*unstructured.Unstructured, error) { + dir := filepath.Join(os.Getenv("LOCALBIN"), "plugin-deps") + pdeps, err := plugins.Dependencies() + if err != nil { + return nil, fmt.Errorf("failed to get plugin dependencies: %w", err) + } + + //get refs from enabledDirs + var refs []string + for _, dep := range pdeps { + if dep.Ref != "" { + refs = append(refs, dep.Ref) + } + } + + return ReadPluginDeps(dir, bsName, bsNamespace, refs) + +} + // ReadPluginDeps reads the plugin dependencies from the specified directory // and returns a slice of unstructured.Unstructured objects. func ReadPluginDeps(rootDir, bsName, bsNamespace string, enabledDirs []string) ([]*unstructured.Unstructured, error) { From 5ceac55e3f0a3ca373d20a20e52ab02d639891b4 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Tue, 29 Apr 2025 11:27:13 +0300 Subject: [PATCH 11/35] initial docs Signed-off-by: gazarenkov --- ...kstage-operator.clusterserviceversion.yaml | 10 +- .../rhdh-default-config_v1_configmap.yaml | 55 ++++++---- config/profile/rhdh/kustomization.yaml | 4 +- .../default-config/dynamic-plugins.yaml | 4 +- docs/dynamic-plugins.md | 103 ++++++++++++++++++ pkg/model/dynamic-plugins.go | 17 +-- pkg/model/dynamic-plugins_test.go | 57 ++++++++++ 7 files changed, 206 insertions(+), 44 deletions(-) create mode 100644 docs/dynamic-plugins.md diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index 2746e95c2..96a1d79b7 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.7 - createdAt: "2025-04-16T13:52:03Z" + createdAt: "2025-04-25T13:38:41Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed @@ -300,8 +300,8 @@ spec: - ALL readOnlyRootFilesystem: true volumeMounts: - - mountPath: plugin-deps - name: plugin-deps + - mountPath: plugin-deps/orchestrator + name: plugin-deps-orchestrator - mountPath: /default-config name: default-config securityContext: @@ -310,8 +310,8 @@ spec: terminationGracePeriodSeconds: 10 volumes: - configMap: - name: rhdh-plugin-deps - name: plugin-deps + name: rhdh-plugin-deps-orchestrator + name: plugin-deps-orchestrator - configMap: name: rhdh-default-config name: default-config diff --git a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml index 9161cad8c..9d17553ab 100644 --- a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml @@ -238,6 +238,8 @@ data: env: - name: NPM_CONFIG_USERCONFIG value: /opt/app-root/src/.npmrc.dynamic-plugins + - name: MAX_ENTRY_SIZE + value: "30000000" volumeMounts: - mountPath: /dynamic-plugins-root name: dynamic-plugins-root @@ -376,37 +378,46 @@ data: metadata: name: default-dynamic-plugins data: - plugin-dependencies: | - # orchestrator + # plugin-dependencies: | + # orchestrator dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml plugins: - - disabled: true #false - integrity: sha512-2aOHDLFrGMAtyHFiyGZwVBZ9Op+TmKYUwfZxwoaGJ1s6JSy/0qgqineEEE0K3dn/f17XBUj+H1dwa5Al598Ugw== - package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-backend-dynamic-1.4.0.tgz + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz" + integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + module: OrchestratorPlugin + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + module: OrchestratorPlugin + path: /orchestrator + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-backend-dynamic-1.5.1.tgz" + integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g== pluginConfig: orchestrator: dataIndexService: url: http://sonataflow-platform-data-index-service - - disabled: true #false - integrity: sha512-2yasbfBZ3iKntArIfK+hk9tvv4b/dy9+WKXOcWIotqkI1gv+Nhvy+m55KAUWi2vmfM0rj3EoG6YP+3Zajn1KyA== - package: https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/1.4.0/backstage-plugin-orchestrator-1.4.0.tgz + dependencies: + - ref: orchestrator + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" + integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== pluginConfig: - dynamicPlugins: - frontend: - red-hat-developer-hub.backstage-plugin-orchestrator: - appIcons: - - importName: OrchestratorIcon - module: OrchestratorPlugin - name: orchestratorIcon - dynamicRoutes: - - importName: OrchestratorPage - menuItem: - icon: orchestratorIcon - text: Orchestrator - module: OrchestratorPlugin - path: /orchestrator + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service route.yaml: |- apiVersion: route.openshift.io/v1 kind: Route diff --git a/config/profile/rhdh/kustomization.yaml b/config/profile/rhdh/kustomization.yaml index 500207b31..7a564d849 100644 --- a/config/profile/rhdh/kustomization.yaml +++ b/config/profile/rhdh/kustomization.yaml @@ -3,4 +3,6 @@ kind: Kustomization resources: - operator -#- plugin-infra +images: +- name: controller + newName: quay.io/gazarenk/backstage-operator diff --git a/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml b/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml index b2ec785ce..0d3b46def 100644 --- a/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml +++ b/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml @@ -25,15 +25,13 @@ kind: ConfigMap metadata: name: default-dynamic-plugins data: -# plugin-dependencies: | -# orchestrator dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml plugins: - disabled: false package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz" - integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== + integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== pluginConfig: dynamicPlugins: frontend: diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md new file mode 100644 index 000000000..4fee057e4 --- /dev/null +++ b/docs/dynamic-plugins.md @@ -0,0 +1,103 @@ +## Dynamic plugins dependency management + +### Overview +Dynamic plugins configured for the Backstage CR may require certain Kubernetes resources to be configured to make the plugin work. These are referred to as 'plugin dependencies'. Starting from version 1.7, it is possible to automatically create these resources when the Backstage CR is applied to the cluster. + +### Profile Configuration +Plugin dependency configuration for a specific profile is done via the `/config/profile/{PROFILE}/operator/plugin-deps` directory. To enable this, the administrator should: +- Create a directory with the name associated with the plugin (for convenience) and place the required resources as Kubernetes manifests in YAML format within it. +- Use Kustomize to generate a ConfigMap with these files. +- Mount this ConfigMap to the Backstage container in the target cluster. + +**Example Directory Structure**: +```txt +config/ + profile/ + rhdh/ + operator/ + kustomization.yaml + example-plugin-patch.yaml + plugin-deps/ + example/ + dep1.yaml + dep2.yaml +``` +Here, dep1.yaml and dep2.yaml are the plugin dependencies for the example plugin. + +**Notes:** + +* If a resource manifest does not specify a namespace, it will be created in the namespace of the Backstage CR. +* Resources may contain {{backstage-name}} and {{backstage-ns}} placeholders, which will be replaced with the name and namespace of the Backstage CR, respectively. + + +The `kustomization.yaml` file should contain the following lines: +```yaml +patches: + - path: example-plugin-patch.yaml + target: + kind: Deployment + name: operator + +configMapGenerator: + - files: + - plugin-deps/example/dep1.yaml + - plugin-deps/example/dep2.yaml + name: plugin-deps-example +``` +The names of patch file and configmap are arbitrary. + +The patch file (`example-plugin-patch.yaml` in this example) should contain the following lines: +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: operator +spec: + template: + spec: + containers: + - name: manager + volumeMounts: + - name: plugin-deps-example + mountPath: plugin-deps/example + volumes: + - name: plugin-deps-example + configMap: + name: plugin-deps-example +``` +Ensure that: +* **spec.template.spec.volumes.configMap.name** matches configMapGenerator.name in the kustomization.yaml file. +* **spec.template.spec.containers.volumeMounts.mountPath** matches the directory path in plugin-deps/example. + + +### Plugin dependencies infrastructure + +If plugin dependencies require infrastructural resources (e.g., RoleBindings, CustomResources), they can be specified in the /config/profile/{PROFILE}/plugin-infra directory. For convenience, these can be grouped per plugin in subdirectories. To create these resources (along with the operator deployment), use the make plugin-infra command. + +**Note**: Be cautious when running this command on a production cluster, as it may reconfigure cluster-scoped resources. + +### Plugin configuration + +To create the plugin dependencies when the Backstage CR is applied, they must be referenced in the **dependencies** field of the plugin configuration. The operator will look for the plugin-deps directory in the default-config directory of the profile and create the resources described in the files within this directory. + +Plugin dependencies can be referenced in the dynamic-plugins' ConfigMap. This can either be part of the profile's default configuration for all Backstage CRs or part of the ConfigMap referenced in the Backstage CR. Starting from version 1.7, plugin dependencies can be included in the dynamic plugin configuration as follows: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: default-dynamic-plugins +data: + dynamic-plugins.yaml: | + includes: + - dynamic-plugins.default.yaml + plugins: + - disabled: false + package: "path-or-url-to-example-plugin" + dependencies: + - ref: example +``` +* In this example, the example dependency is referenced. +* The operator will look for the plugin-deps/example directory in the profile and create the resources described in the files within this directory. + +This ensures that all required resources for the plugin are automatically created when the Backstage CR is applied. diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index 5a2f2aab5..7696bbd2f 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -42,6 +42,8 @@ type DynamicPlugins struct { } type DynaPluginsConfig struct { + // we do not really support Includes here, that's what is processed by the installation script + // in the dynamic-plugins container. Keeping it here for the sake of completeness Includes []string `yaml:"includes"` Plugins []DynaPlugin `yaml:"plugins"` } @@ -159,6 +161,7 @@ func (p *DynamicPlugins) setMetaInfo(backstage bsv1.Backstage, scheme *runtime.S setMetaInfo(p.ConfigMap, backstage, scheme) } +// Dependencies returns a list of plugin dependencies func (p *DynamicPlugins) Dependencies() ([]PluginDependency, error) { ps, err := p.pluginsFromConfigMap() if err != nil { @@ -176,19 +179,9 @@ func (p *DynamicPlugins) Dependencies() ([]PluginDependency, error) { } return result, nil - - //if p.ConfigMap == nil { - // return []string{} - //} - // - //data := p.ConfigMap.Data[EnabledPluginsDepsFile] - //if data != "" { - // return strings.Split(data, "\n") - //} - // - //return []string{} } +// returns a list of plugins from the configMap func (p *DynamicPlugins) pluginsFromConfigMap() ([]DynaPlugin, error) { if p.ConfigMap == nil { return nil, fmt.Errorf("dynamic plugins configMap is not set") @@ -199,7 +192,6 @@ func (p *DynamicPlugins) pluginsFromConfigMap() ([]DynaPlugin, error) { return nil, fmt.Errorf("dynamic plugins configMap does not contain %s key", DynamicPluginsFile) } - //var plugins []DynaPlugin var pluginsConfig DynaPluginsConfig err := yaml.Unmarshal([]byte(data), &pluginsConfig) if err != nil { @@ -207,7 +199,6 @@ func (p *DynamicPlugins) pluginsFromConfigMap() ([]DynaPlugin, error) { } return pluginsConfig.Plugins, nil - } // returns initContainer supposed to initialize DynamicPlugins diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index 5d7f0c9ab..2e2e50f09 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -251,3 +251,60 @@ plugins: assert.Equal(t, "value3", pluginB.PluginConfig["key3"]) assert.Empty(t, pluginB.Dependencies) } + +func TestDynamicPluginsDependencies(t *testing.T) { + // Case 1: Plugins with dependencies + yamlDataWithDeps := ` +plugins: + - package: "plugin-a" + disabled: false + dependencies: + - ref: "dependency-1" + - ref: "dependency-2" + - package: "plugin-b" + disabled: false + dependencies: + - ref: "dependency-3" + - package: "plugin-disabled" + disabled: true + dependencies: + - ref: "dependency-4" +` + + dpWithDeps := &DynamicPlugins{ + ConfigMap: &corev1.ConfigMap{ + Data: map[string]string{ + DynamicPluginsFile: yamlDataWithDeps, + }, + }, + } + + deps, err := dpWithDeps.Dependencies() + assert.NoError(t, err) + assert.Equal(t, 3, len(deps)) + assert.Equal(t, "dependency-1", deps[0].Ref) + assert.Equal(t, "dependency-2", deps[1].Ref) + assert.Equal(t, "dependency-3", deps[2].Ref) + + // Case 2: Plugins without dependencies + yamlDataWithoutDeps := ` +plugins: + - package: "plugin-c" + disabled: false + - package: "plugin-d" + disabled: false +` + + dpWithoutDeps := &DynamicPlugins{ + ConfigMap: &corev1.ConfigMap{ + Data: map[string]string{ + DynamicPluginsFile: yamlDataWithoutDeps, + }, + }, + } + + deps, err = dpWithoutDeps.Dependencies() + assert.NoError(t, err) + assert.NotNil(t, deps) + assert.Equal(t, 0, len(deps)) // Ensure it returns an empty slice, not nil +} From 5d3b7d4b3fc08594adf636169a83bbaa8e7c55ff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 29 Apr 2025 08:33:50 +0000 Subject: [PATCH 12/35] Regenerate bundle/installer manifests Co-authored-by: gazarenkov --- ...kstage-operator.clusterserviceversion.yaml | 2 +- ...kstage-operator.clusterserviceversion.yaml | 2 +- .../rhdh-default-config_v1_configmap.yaml | 2 - config/profile/rhdh/kustomization.yaml | 3 +- dist/rhdh/install.yaml | 97 +++++++++++++------ 5 files changed, 70 insertions(+), 36 deletions(-) diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index 20c88cc12..bd9a5a557 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -35,7 +35,7 @@ metadata: } } ] - createdAt: "2025-04-23T11:45:47Z" + createdAt: "2025-04-29T08:33:43Z" description: Backstage Operator operators.operatorframework.io/builder: operator-sdk-v1.37.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index 96a1d79b7..a3cd8bd08 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.7 - createdAt: "2025-04-25T13:38:41Z" + createdAt: "2025-04-29T08:33:45Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed diff --git a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml index 9d17553ab..041692c35 100644 --- a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml @@ -378,8 +378,6 @@ data: metadata: name: default-dynamic-plugins data: - # plugin-dependencies: | - # orchestrator dynamic-plugins.yaml: | includes: - dynamic-plugins.default.yaml diff --git a/config/profile/rhdh/kustomization.yaml b/config/profile/rhdh/kustomization.yaml index 7a564d849..67253cc6d 100644 --- a/config/profile/rhdh/kustomization.yaml +++ b/config/profile/rhdh/kustomization.yaml @@ -5,4 +5,5 @@ resources: - operator images: - name: controller - newName: quay.io/gazarenk/backstage-operator + newName: quay.io/rhdh/rhdh-rhel9-operator + newTag: "1.7" diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index 689cd162f..fac3e6fc8 100644 --- a/dist/rhdh/install.yaml +++ b/dist/rhdh/install.yaml @@ -1861,37 +1861,72 @@ data: securityContext: # any group id fsGroup: 1001 - dynamic-plugins.yaml: "#apiVersion: v1\n#kind: ConfigMap\n#metadata:\n# name: default-dynamic-plugins - # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name\n#data:\n# - \ \"dynamic-plugins.yaml\": |\n# ###########################################################################################################\n# - \ # /!\\ WARNING\n# #\n# # This is the default dynamic plugins configuration - file created and managed by the Operator for your CR.\n# # Do NOT edit this - manually in the Cluster, as your changes will be overridden by the Operator upon - the\n# # next reconciliation.\n# # If you want to customize the dynamic - plugins, you should create your own dynamic-plugins ConfigMap\n# # and reference - it in your CR.\n# # See https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.4/html/installing_and_viewing_plugins_in_red_hat_developer_hub/rhdh-installing-rhdh-plugins_title-plugins-rhdh-about#proc-config-dynamic-plugins-rhdh-operator_rhdh-installing-rhdh-plugins\n# - \ # for more details or https://github.com/redhat-developer/rhdh-operator/blob/main/examples/rhdh-cr.yaml\n# - \ # for an example.\n# ###########################################################################################################\n# - \ includes:\n# - dynamic-plugins.default.yaml\n# plugins: []\n#---\napiVersion: - v1\nkind: ConfigMap\nmetadata:\n name: default-dynamic-plugins\ndata:\n plugin-dependencies: - |\n# orchestrator\n dynamic-plugins.yaml: |\n includes:\n - dynamic-plugins.default.yaml\n - \ plugins:\n - disabled: false\n package: \"https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz\"\n - \ integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== - \ \n pluginConfig:\n dynamicPlugins:\n frontend:\n - \ red-hat-developer-hub.backstage-plugin-orchestrator:\n appIcons:\n - \ - importName: OrchestratorIcon\n module: - OrchestratorPlugin\n name: orchestratorIcon\n dynamicRoutes:\n - \ - importName: OrchestratorPage\n menuItem:\n - \ icon: orchestratorIcon\n text: - Orchestrator\n module: OrchestratorPlugin\n path: - /orchestrator\n - disabled: false\n package: \"https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-backend-dynamic-1.5.1.tgz\"\n - \ integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g==\n - \ pluginConfig:\n orchestrator:\n dataIndexService:\n - \ url: http://sonataflow-platform-data-index-service\n - disabled: - false\n package: \"https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz\"\n - \ integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ==\n - \ pluginConfig:\n orchestrator:\n dataIndexService:\n - \ url: http://sonataflow-platform-data-index-service\n" + dynamic-plugins.yaml: | + #apiVersion: v1 + #kind: ConfigMap + #metadata: + # name: default-dynamic-plugins # must be the same as (deployment.yaml).spec.template.spec.volumes.name.dynamic-plugins-conf.configMap.name + #data: + # "dynamic-plugins.yaml": | + # ########################################################################################################### + # # /!\ WARNING + # # + # # This is the default dynamic plugins configuration file created and managed by the Operator for your CR. + # # Do NOT edit this manually in the Cluster, as your changes will be overridden by the Operator upon the + # # next reconciliation. + # # If you want to customize the dynamic plugins, you should create your own dynamic-plugins ConfigMap + # # and reference it in your CR. + # # See https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.4/html/installing_and_viewing_plugins_in_red_hat_developer_hub/rhdh-installing-rhdh-plugins_title-plugins-rhdh-about#proc-config-dynamic-plugins-rhdh-operator_rhdh-installing-rhdh-plugins + # # for more details or https://github.com/redhat-developer/rhdh-operator/blob/main/examples/rhdh-cr.yaml + # # for an example. + # ########################################################################################################### + # includes: + # - dynamic-plugins.default.yaml + # plugins: [] + #--- + apiVersion: v1 + kind: ConfigMap + metadata: + name: default-dynamic-plugins + data: + dynamic-plugins.yaml: | + includes: + - dynamic-plugins.default.yaml + plugins: + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz" + integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + module: OrchestratorPlugin + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + module: OrchestratorPlugin + path: /orchestrator + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-backend-dynamic-1.5.1.tgz" + integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g== + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service + dependencies: + - ref: orchestrator + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" + integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service route.yaml: |- apiVersion: route.openshift.io/v1 kind: Route From 2c907063aa2b17a1b3515082bb90fac0a0ad2dfc Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Tue, 29 Apr 2025 12:45:19 +0300 Subject: [PATCH 13/35] docs Signed-off-by: gazarenkov --- docs/configuration.md | 30 +++++++++++++++--------------- docs/dynamic-plugins.md | 14 ++++++++------ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index d799b563c..20df842ea 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -12,21 +12,21 @@ The Default Configuration defines the structure of all Backstage instances withi ### Default Configuration Files -| Key/File Name | Object Kind | Object Name | Mandatory | Multi | Version | Notes | -|----------------------|--------------------------------|--------------------------------------------|--------------|-------|---------|-------------------------------------------------| -| deployment.yaml | appsv1.Deployment | backstage- | Yes | No | >=0.1.x | Backstage deployment | -| service.yaml | corev1.Service | backstage- | Yes | No | >=0.1.x | Backstage Service | -| db-statefulset.yaml | appsv1.StatefulSet | backstage-psql- | For local DB | No | >=0.1.x | PostgreSQL StatefulSet | -| db-service.yaml | corev1.Service | backstage-psql- | For local DB | No | >=0.1.x | PostgreSQL Service | -| db-secret.yaml | corev1.Secret | backstage-psql-secret- | For local DB | No | >=0.1.x | Secret to connect Backstage to PGSQL | -| route.yaml | openshift.Route | backstage- | No (for OCP) | No | >=0.1.x | Route exposing Backstage service | -| app-config.yaml | corev1.ConfigMap | backstage-appconfig- | No | No | >=0.2.x | Backstage app-config.yaml | -| configmap-files.yaml | corev1.ConfigMap | backstage-files- | No | No | >=0.2.x | Backstage config file inclusions from configMap | -| configmap-envs.yaml | corev1.ConfigMap | backstage-envs- | No | No | >=0.2.x | Backstage environment variables from ConfigMap | -| secret-files.yaml | []corev1.Secret | backstage-files- | No | Yes | >=0.2.x | Backstage config file inclusions from Secret | -| secret-envs.yaml | []corev1.Secret | backstage-envs- | No | Yes | >=0.2.x | Backstage environment variables from Secret | -| dynamic-plugins.yaml | corev1.ConfigMap | backstage-dynamic-plugins- | No | No | >=0.2.x | Dynamic plugins configuration | -| pvcs.yaml | []corev1.PersistentVolumeClaim | backstage-<cr-name>-<pvc-name> | No | Yes | >=0.4.x | List of PVC objects to be mounted to containers | +| Key/File Name | Object Kind | Object Name | Mandatory | Multi | Version | Notes | +|------------------------------------------|--------------------------------|--------------------------------------------|--------------|-------|---------|-------------------------------------------------| +| deployment.yaml | appsv1.Deployment | backstage- | Yes | No | >=0.1.x | Backstage deployment | +| service.yaml | corev1.Service | backstage- | Yes | No | >=0.1.x | Backstage Service | +| db-statefulset.yaml | appsv1.StatefulSet | backstage-psql- | For local DB | No | >=0.1.x | PostgreSQL StatefulSet | +| db-service.yaml | corev1.Service | backstage-psql- | For local DB | No | >=0.1.x | PostgreSQL Service | +| db-secret.yaml | corev1.Secret | backstage-psql-secret- | For local DB | No | >=0.1.x | Secret to connect Backstage to PGSQL | +| route.yaml | openshift.Route | backstage- | No (for OCP) | No | >=0.1.x | Route exposing Backstage service | +| app-config.yaml | corev1.ConfigMap | backstage-appconfig- | No | No | >=0.2.x | Backstage app-config.yaml | +| configmap-files.yaml | corev1.ConfigMap | backstage-files- | No | No | >=0.2.x | Backstage config file inclusions from configMap | +| configmap-envs.yaml | corev1.ConfigMap | backstage-envs- | No | No | >=0.2.x | Backstage environment variables from ConfigMap | +| secret-files.yaml | []corev1.Secret | backstage-files- | No | Yes | >=0.2.x | Backstage config file inclusions from Secret | +| secret-envs.yaml | []corev1.Secret | backstage-envs- | No | Yes | >=0.2.x | Backstage environment variables from Secret | +| [dynamic-plugins.yaml](#dynamic-plugins) | corev1.ConfigMap | backstage-dynamic-plugins- | No | No | >=0.2.x | Dynamic plugins configuration | +| pvcs.yaml | []corev1.PersistentVolumeClaim | backstage-<cr-name>-<pvc-name> | No | Yes | >=0.4.x | List of PVC objects to be mounted to containers | **Meanings of "Mandatory" Column:** - **Yes** - Must be configured; deployment will fail otherwise. diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index 4fee057e4..5ad8ad482 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -6,8 +6,8 @@ Dynamic plugins configured for the Backstage CR may require certain Kubernetes r ### Profile Configuration Plugin dependency configuration for a specific profile is done via the `/config/profile/{PROFILE}/operator/plugin-deps` directory. To enable this, the administrator should: - Create a directory with the name associated with the plugin (for convenience) and place the required resources as Kubernetes manifests in YAML format within it. -- Use Kustomize to generate a ConfigMap with these files. -- Mount this ConfigMap to the Backstage container in the target cluster. +- Create/modify kustomization.yaml to generate a ConfigMap with these files. +- Create/modify a patch which make Kustomize mount this ConfigMap to the Backstage container. **Example Directory Structure**: ```txt @@ -22,14 +22,13 @@ config/ dep1.yaml dep2.yaml ``` -Here, dep1.yaml and dep2.yaml are the plugin dependencies for the example plugin. +Here, **dep1.yaml** and **dep2.yaml** are the plugin dependencies for the example plugin. **Notes:** * If a resource manifest does not specify a namespace, it will be created in the namespace of the Backstage CR. * Resources may contain {{backstage-name}} and {{backstage-ns}} placeholders, which will be replaced with the name and namespace of the Backstage CR, respectively. - The `kustomization.yaml` file should contain the following lines: ```yaml patches: @@ -53,7 +52,10 @@ kind: Deployment metadata: name: operator spec: - template: + selector: + matchLabels: + app: backstage-operator + template: spec: containers: - name: manager @@ -80,7 +82,7 @@ If plugin dependencies require infrastructural resources (e.g., RoleBindings, Cu To create the plugin dependencies when the Backstage CR is applied, they must be referenced in the **dependencies** field of the plugin configuration. The operator will look for the plugin-deps directory in the default-config directory of the profile and create the resources described in the files within this directory. -Plugin dependencies can be referenced in the dynamic-plugins' ConfigMap. This can either be part of the profile's default configuration for all Backstage CRs or part of the ConfigMap referenced in the Backstage CR. Starting from version 1.7, plugin dependencies can be included in the dynamic plugin configuration as follows: +Plugin dependencies can be referenced in the dynamic-plugins' ConfigMap. This can either be part of the profile's [default configuration](configuration.md/#default-configuration-files) for all Backstage CRs or part of the [ConfigMap referenced in the Backstage CR](configuration.md/#dynamic-plugins). Starting from version 1.7, plugin dependencies can be included in the dynamic plugin configuration as follows: ```yaml apiVersion: v1 From fd3c0f30c0671f424024d843b1156832cbf4ecfd Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Tue, 29 Apr 2025 14:11:35 +0300 Subject: [PATCH 14/35] removed sample-greetings Signed-off-by: gazarenkov --- .../profile/rhdh/operator/kustomization.yaml | 2 - .../orchestrator/sample-greetings.yaml | 130 ------------------ 2 files changed, 132 deletions(-) delete mode 100644 config/profile/rhdh/operator/plugin-deps/orchestrator/sample-greetings.yaml diff --git a/config/profile/rhdh/operator/kustomization.yaml b/config/profile/rhdh/operator/kustomization.yaml index 916109806..9e244e7af 100644 --- a/config/profile/rhdh/operator/kustomization.yaml +++ b/config/profile/rhdh/operator/kustomization.yaml @@ -42,5 +42,3 @@ configMapGenerator: name: default-config - files: - plugin-deps/orchestrator/sonataflow.yaml - - plugin-deps/orchestrator/sample-greetings.yaml - name: plugin-deps-orchestrator diff --git a/config/profile/rhdh/operator/plugin-deps/orchestrator/sample-greetings.yaml b/config/profile/rhdh/operator/plugin-deps/orchestrator/sample-greetings.yaml deleted file mode 100644 index 7c92f2c23..000000000 --- a/config/profile/rhdh/operator/plugin-deps/orchestrator/sample-greetings.yaml +++ /dev/null @@ -1,130 +0,0 @@ -apiVersion: v1 -data: - greeting.sw.input-schema.json: | - { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "language": { - "title": "Language", - "description": "Language to greet", - "type": "string", - "enum": ["English", "Spanish"], - "default": "English" - } - } - } - workflow-output-schema.json: |- - { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "WorkflowResult", - "description": "Schema of workflow output", - "type": "object", - "properties": { - "result": { - "$ref": "../shared/schemas/workflow-result-schema.json", - "type": "object" - } - } - } -kind: ConfigMap -metadata: - name: 01-greeting-resources-schemas ---- -apiVersion: sonataflow.org/v1alpha08 -kind: SonataFlow -metadata: - annotations: - sonataflow.org/description: YAML based greeting workflow - sonataflow.org/expressionLang: jq - sonataflow.org/profile: gitops - sonataflow.org/version: "1.0" - labels: - app: greeting - sonataflow.org/workflow-app: greeting - name: greeting -spec: - flow: - annotations: - - workflow-type/infrastructure - dataInputSchema: - failOnValidationErrors: true - schema: schemas/greeting.sw.input-schema.json - functions: - - name: greetFunction - operation: sysout - type: custom - - name: successResult - operation: '{ "result": { "message": "Greeting workflow completed successfully", "outputs":[ { "key":"Selected language", "value": .language }, { "key":"Greeting message", "value": .greeting } ] } }' - type: expression - start: - stateName: ChooseOnLanguage - states: - - dataConditions: - - condition: .language == "English" - transition: - nextState: GreetInEnglish - - condition: .language == "Spanish" - transition: - nextState: GreetInSpanish - defaultCondition: - transition: - nextState: GreetInEnglish - name: ChooseOnLanguage - type: switch - - data: - greeting: Hello from YAML Workflow - name: GreetInEnglish - transition: - nextState: GreetPerson - type: inject - - data: - greeting: Saludos desde YAML Workflow - name: GreetInSpanish - transition: - nextState: GreetPerson - type: inject - - actionMode: sequential - actions: - - actionDataFilter: - useResults: true - functionRef: - arguments: - message: .greeting - invoke: sync - refName: greetFunction - name: greetAction - - actionDataFilter: - useResults: true - functionRef: - invoke: sync - refName: successResult - name: setOutput - end: - terminate: true - name: GreetPerson - type: operation - podTemplate: - container: - resources: {} - image: quay.io/orchestrator/serverless-workflow-greeting:96d772297d3ee964ab9775d7aae7dbbd4f070dd7 - resources: - configMaps: - - configMap: - name: 01-greeting-resources-schemas - workflowPath: schemas - persistence: - postgresql: - secretRef: - name: backstage-psql-secret-{{backstage-name}} - userKey: POSTGRES_USER #hardcoded - passwordKey: POSTGRES_PASSWORD #hardcoded - serviceRef: - name: backstage-psql-{{backstage-name}} # hardcoded backstage-psql-{{cr-name}} - namespace: {{backstage-ns}} # hardcoded {{cr-namespace}} - databaseName: backstage_plugin_orchestrator #hardcoded - port: 5432 - databaseSchema: greeting -status: - address: {} - From f2c6beacd6462630ede29a3071397467e5f7d526 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Tue, 29 Apr 2025 14:24:47 +0300 Subject: [PATCH 15/35] disabled orch plugin Signed-off-by: gazarenkov --- .../rhdh/operator/default-config/dynamic-plugins.yaml | 6 +++--- pkg/model/dynamic-plugins.go | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml b/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml index 0d3b46def..c761a798d 100644 --- a/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml +++ b/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml @@ -29,7 +29,7 @@ data: includes: - dynamic-plugins.default.yaml plugins: - - disabled: false + - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz" integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== pluginConfig: @@ -47,7 +47,7 @@ data: text: Orchestrator module: OrchestratorPlugin path: /orchestrator - - disabled: false + - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-backend-dynamic-1.5.1.tgz" integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g== pluginConfig: @@ -56,7 +56,7 @@ data: url: http://sonataflow-platform-data-index-service dependencies: - ref: orchestrator - - disabled: false + - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== pluginConfig: diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index 7696bbd2f..e11d56b83 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -2,9 +2,10 @@ package model import ( "fmt" - "gopkg.in/yaml.v2" "os" + "gopkg.in/yaml.v2" + "golang.org/x/exp/maps" "sigs.k8s.io/controller-runtime/pkg/client" From fc526dc0506f92630a6136b06b47d071670ab248 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Tue, 29 Apr 2025 18:56:22 +0300 Subject: [PATCH 16/35] fix dynamic-plugins Signed-off-by: gazarenkov --- internal/controller/backstage_controller.go | 2 +- pkg/model/dynamic-plugins.go | 3 ++- pkg/model/dynamic-plugins_test.go | 9 +++++++++ pkg/model/runtime.go | 4 ++-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/internal/controller/backstage_controller.go b/internal/controller/backstage_controller.go index 42516ccd6..a8ba8cfcb 100644 --- a/internal/controller/backstage_controller.go +++ b/internal/controller/backstage_controller.go @@ -107,7 +107,7 @@ func (r *BackstageReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } // Apply the plugin dependencies - if err := r.applyPluginDeps(ctx, req.NamespacedName, *bsModel.DynamicPlugins); err != nil { + if err := r.applyPluginDeps(ctx, req.NamespacedName, bsModel.DynamicPlugins); err != nil { return ctrl.Result{}, errorAndStatus(&backstage, "failed to apply plugin dependencies", err) } diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index e11d56b83..06d50bf6f 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -91,6 +91,7 @@ func addDynamicPluginsFromSpec(spec bsv1.BackstageSpec, model *BackstageModel) e dp.Name, ic.WorkingDir, DynamicPluginsFile, true, maps.Keys(dp.Data)) } + // if default dynamic-plugins set, the value from model.DynamicPlugins will differ from the one in model.RuntimeObjects model.DynamicPlugins.ConfigMap = &dp return nil @@ -125,7 +126,7 @@ func (p *DynamicPlugins) addToModel(model *BackstageModel, backstage bsv1.Backst } } model.setRuntimeObject(p) - model.DynamicPlugins = p + model.DynamicPlugins = *p return true, nil } diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index 2e2e50f09..f9ef488f6 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -190,10 +190,19 @@ plugins: assert.NoError(t, err) assert.NotNil(t, model) + + // dependencies from external config + // - ref: "dependency-1" + // - ref: "dependency-2" deps, err := model.DynamicPlugins.Dependencies() assert.NoError(t, err) assert.Equal(t, 2, len(deps)) + // dependencies from default config = [] + depends, err := (model.getRuntimeObjectByType(&DynamicPlugins{})).(*DynamicPlugins).Dependencies() + assert.NoError(t, err) + assert.Equal(t, 0, len(depends)) + } func initContainer(model *BackstageModel) *corev1.Container { diff --git a/pkg/model/runtime.go b/pkg/model/runtime.go index 7f03034c2..0b68a6034 100644 --- a/pkg/model/runtime.go +++ b/pkg/model/runtime.go @@ -45,7 +45,7 @@ type BackstageModel struct { route *BackstageRoute appConfig *AppConfig - DynamicPlugins *DynamicPlugins + DynamicPlugins DynamicPlugins RuntimeObjects []RuntimeObject @@ -102,7 +102,7 @@ func InitObjects(ctx context.Context, backstage bsv1.Backstage, externalConfig E lg := log.FromContext(ctx) lg.V(1) - model := &BackstageModel{RuntimeObjects: make([]RuntimeObject, 0), ExternalConfig: externalConfig, localDbEnabled: backstage.Spec.IsLocalDbEnabled(), isOpenshift: isOpenshift, DynamicPlugins: &DynamicPlugins{}} + model := &BackstageModel{RuntimeObjects: make([]RuntimeObject, 0), ExternalConfig: externalConfig, localDbEnabled: backstage.Spec.IsLocalDbEnabled(), isOpenshift: isOpenshift, DynamicPlugins: DynamicPlugins{}} // looping through the registered runtimeConfig objects initializing the model for _, conf := range runtimeConfig { From 87dc7fa6fdc1654dda6911611de80a2766ae0d9b Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Thu, 1 May 2025 12:00:46 +0300 Subject: [PATCH 17/35] orch doc and example Signed-off-by: gazarenkov --- docs/configuration.md | 3 ++ docs/dynamic-plugins.md | 49 ++++++++++++++++++- examples/orchestrator.yaml | 99 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 examples/orchestrator.yaml diff --git a/docs/configuration.md b/docs/configuration.md index 20df842ea..cb75d09f2 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -507,6 +507,9 @@ spec: application: dynamicPluginsConfigMapName: "dynamic-plugins-config" ``` +Starting from version **0.7.0**, the Operator supports dynamic plugins dependencies. For more details, refer to [Dynamic Plugins Dependencies](dynamic-plugins.md). + +```yaml #### Deployment Parameters diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index 5ad8ad482..e2133f404 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -74,7 +74,7 @@ Ensure that: ### Plugin dependencies infrastructure -If plugin dependencies require infrastructural resources (e.g., RoleBindings, CustomResources), they can be specified in the /config/profile/{PROFILE}/plugin-infra directory. For convenience, these can be grouped per plugin in subdirectories. To create these resources (along with the operator deployment), use the make plugin-infra command. +If plugin dependencies require infrastructural resources (e.g., RoleBindings, CustomResources) and if the User (Administrator) wants it to be deployed (see Note below), they can be specified in the /config/profile/{PROFILE}/plugin-infra directory. For convenience, these can be grouped per plugin in subdirectories. To create these resources (along with the operator deployment), use the make plugin-infra command. **Note**: Be cautious when running this command on a production cluster, as it may reconfigure cluster-scoped resources. @@ -103,3 +103,50 @@ data: * The operator will look for the plugin-deps/example directory in the profile and create the resources described in the files within this directory. This ensures that all required resources for the plugin are automatically created when the Backstage CR is applied. + +### Example: Orchestrator plugin dependencies +The orchestrator plugin (as of v1.5.1) consists of three dynamic plugins: +- orchestrator-backend +- orchestrator-frontend +- scaffolder-backend-module +See [example](/examples/orchestrator.yaml) for a complete configuration of the orchestrator plugin. + + +The orchestrator plugin has the following dependencies: +- A Sonataflowplatform custom resource - created in the namespace of the Backstage CR. +- Knativeeventing and Knativeserving custom resources to be created in the knative-eventing and knative-serving namespaces respectively. +- A set of NetworkPolicies to communicate to Knative resources created in the namespace of Backstage CR. +See [profile/rhdh/operator/plugin-deps/orchestrator](/config/profile/rhdh/operator/plugin-deps/orchestrator) + +The orchestrator-backend plugin uses the service sonataflow-platform-data-index-service, which is created by the SonataFlowPlatform CR. This service is used to communicate with the SonataFlow platform. + +**Important:** The sonataflowplatform CR contains dataIndex service that requires PostgreSQL database. + +```yaml + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: backstage-psql-{{backstage-name}} + namespace: {{backstage-ns}} + databaseName: backstage_plugin_orchestrator +``` + +Current **default** implementation of the orchestrator plugin dependencies uses: +- the PstgreSQL database created bt Backstage for Orchestrator plugin, named **backstage_plugin_orchestrator** +- the Secret created by Backstage operator for the PstgreSQL with **POSTGRES_USER** and **POSTGRES_PASSWORD** keys as the database credentials in the Backstage CR namespace. +- the Service created by Backstage operator for the PostgreSQL database with the name **backstage-psql-{{backstage-name}}** in the Backstage CR namespace. + +**Note:** This default implementation is not recommended for production. For production environments, you should configure an external database connection instead (**TODO: Provide an example with an external database**). + +**Known issue:** +Since DB secret with credentials is automatically generated by the Operator every time new Backstage CR created and sonataflowplatform is not recreated and so uses the "old" secret, the orchestrator plugin will not work properly. To resolve this, the user must manually recreate the SonataFlowPlatform CR or its pods. + +The SonataFlowPlatform CR requires the SonataFlow operator to be installed. In an OpenShift environment, it is done by installing the OpenShift Serverless Operator and the OpenShift Serverless Logic Operator. +Additionally, to enable the Backstage operator to work with the SonataFlow platform, its ServiceAccount must be granted the appropriate permissions. + +These manifests are defined as plugin infrastructure in the profile/rhdh/operator/plugin-infra/orchestrator directory. +**Note:** Using plugin infrastructure (plugin-infra) can be risky in production, as it modifies cluster-scoped resources. \ No newline at end of file diff --git a/examples/orchestrator.yaml b/examples/orchestrator.yaml new file mode 100644 index 000000000..4e60ef207 --- /dev/null +++ b/examples/orchestrator.yaml @@ -0,0 +1,99 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: orchestrator-plugin +data: + dynamic-plugins.yaml: | + includes: + - dynamic-plugins.default.yaml + plugins: + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz" + integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-orchestrator: + appIcons: + - importName: OrchestratorIcon + module: OrchestratorPlugin + name: orchestratorIcon + dynamicRoutes: + - importName: OrchestratorPage + menuItem: + icon: orchestratorIcon + text: Orchestrator + module: OrchestratorPlugin + path: /orchestrator + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-backend-dynamic-1.5.1.tgz" + integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g== + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service + dependencies: + - ref: orchestrator + - disabled: false + package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" + integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== + pluginConfig: + orchestrator: + dataIndexService: + url: http://sonataflow-platform-data-index-service +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: app-config-rhdh +data: + app-config-rhdh.yaml: |- + app: + title: Red Hat Developer Hub + backend: + cors: + credentials: true + methods: + - GET + - POST + - PUT + - DELETE + csp: + script-src: ["'self'", "'unsafe-inline'", "'unsafe-eval'"] + script-src-elem: ["'self'", "'unsafe-inline'", "'unsafe-eval'"] + connect-src: ["'self'", 'http:', 'https:', 'data:'] + auth: + externalAccess: + - type: legacy + options: + subject: legacy-default-config + secret: "${BACKEND_SECRET}" + auth: + environment: development + providers: + guest: + # using the guest user to query the '/api/dynamic-plugins-info/loaded-plugins' endpoint. + dangerouslyAllowOutsideDevelopment: true +--- +apiVersion: v1 +kind: Secret +metadata: + name: rhdh-secrets +type: Opaque +stringData: + #ROUTE_URL: "https://backstage-bs1-backstage.apps.rosa.pvmvb-xo4tu-ri7.1py9.p3.openshiftapps.com" + BACKEND_SECRET: "R2FxRVNrcmwzYzhhN3l0V1VRcnQ3L1pLT09WaVhDNUEK" +--- +apiVersion: rhdh.redhat.com/v1alpha3 +kind: Backstage +metadata: + name: orchestrator +spec: + application: + appConfig: + configMaps: + - name: app-config-rhdh + extraEnvs: + secrets: + - name: rhdh-secrets + dynamicPluginsConfigMapName: orchestrator-plugin From 6b6c5fb959e0b43128a28e1776619d93494fc299 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Thu, 1 May 2025 12:22:12 +0300 Subject: [PATCH 18/35] merge Signed-off-by: gazarenkov --- api/v1alpha1/zz_generated.deepcopy.go | 2 +- api/v1alpha2/zz_generated.deepcopy.go | 2 +- api/v1alpha3/zz_generated.deepcopy.go | 2 +- integration_tests/suite_test.go | 6 ------ pkg/model/dynamic-plugins_test.go | 2 +- 5 files changed, 4 insertions(+), 10 deletions(-) diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 9b7acdd38..ca4d7a346 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha1 import ( - "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index c3ecd6b25..1357c08bd 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha2 import ( - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index b315b789e..6613a8896 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha3 import ( - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/integration_tests/suite_test.go b/integration_tests/suite_test.go index 51d7ac611..c8c4338d7 100644 --- a/integration_tests/suite_test.go +++ b/integration_tests/suite_test.go @@ -92,12 +92,6 @@ var _ = BeforeSuite(func() { testEnv.UseExistingCluster = ptr.To(boolValue) } } - if !*testEnv.UseExistingCluster { - // force k8s platform if not use existing cluster - // this is needed for instance for the case when we run tests - // on test env but OCP cluster is configured as current - os.Setenv(utils.PlatformEnvVar, "k8s") - } if val, ok := os.LookupEnv("USE_EXISTING_CONTROLLER"); ok { boolValue, err := strconv.ParseBool(val) diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index 747ae4d21..5c92544c8 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -188,7 +188,7 @@ plugins: Data: map[string]string{DynamicPluginsFile: yamlData}, } - model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, false, testObj.scheme) + model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) assert.NotNil(t, model) From 817a52f95eb93ceded64938b5a833bd206794599 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Thu, 1 May 2025 13:15:51 +0300 Subject: [PATCH 19/35] fix pluginsFromConfigMap Signed-off-by: gazarenkov --- pkg/model/dynamic-plugins.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index 06d50bf6f..78c7236f9 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -186,12 +186,12 @@ func (p *DynamicPlugins) Dependencies() ([]PluginDependency, error) { // returns a list of plugins from the configMap func (p *DynamicPlugins) pluginsFromConfigMap() ([]DynaPlugin, error) { if p.ConfigMap == nil { - return nil, fmt.Errorf("dynamic plugins configMap is not set") + return []DynaPlugin{}, nil } data := p.ConfigMap.Data[DynamicPluginsFile] if data == "" { - return nil, fmt.Errorf("dynamic plugins configMap does not contain %s key", DynamicPluginsFile) + return []DynaPlugin{}, nil } var pluginsConfig DynaPluginsConfig From 99f2862396b86eb7c76f639b9f57ba5b8241c073 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Thu, 1 May 2025 22:53:57 +0300 Subject: [PATCH 20/35] fix kustomization.yaml Signed-off-by: gazarenkov --- .../backstage.io/operator/default-config/deployment.yaml | 2 +- config/profile/rhdh/operator/kustomization.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config/profile/backstage.io/operator/default-config/deployment.yaml b/config/profile/backstage.io/operator/default-config/deployment.yaml index 16609f6f5..eac3a90dc 100644 --- a/config/profile/backstage.io/operator/default-config/deployment.yaml +++ b/config/profile/backstage.io/operator/default-config/deployment.yaml @@ -14,7 +14,7 @@ spec: spec: containers: - name: backstage-backend - image: ghcr.io/backstage/backstage:1.28.4 + image: ghcr.io/backstage/backstage:1.38.1 imagePullPolicy: IfNotPresent command: - "node" diff --git a/config/profile/rhdh/operator/kustomization.yaml b/config/profile/rhdh/operator/kustomization.yaml index 9e244e7af..0b4dc4bb6 100644 --- a/config/profile/rhdh/operator/kustomization.yaml +++ b/config/profile/rhdh/operator/kustomization.yaml @@ -42,3 +42,4 @@ configMapGenerator: name: default-config - files: - plugin-deps/orchestrator/sonataflow.yaml + name: plugin-deps-orchestrator From 5db55380630d3220f675fc197ec459eb1a1cb503 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 May 2025 19:54:57 +0000 Subject: [PATCH 21/35] Regenerate bundle/installer manifests Co-authored-by: gazarenkov --- api/v1alpha1/zz_generated.deepcopy.go | 2 +- api/v1alpha2/zz_generated.deepcopy.go | 2 +- api/v1alpha3/zz_generated.deepcopy.go | 2 +- ...backstage-default-config_v1_configmap.yaml | 2 +- ...kstage-operator.clusterserviceversion.yaml | 2 +- ...kstage-operator.clusterserviceversion.yaml | 4 +- .../rhdh-default-config_v1_configmap.yaml | 6 +- ...plugin-deps-orchestrator_v1_configmap.yaml | 131 ----------------- dist/backstage.io/install.yaml | 2 +- dist/rhdh/install.yaml | 137 +----------------- 10 files changed, 14 insertions(+), 276 deletions(-) diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ca4d7a346..9b7acdd38 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha1 import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index 1357c08bd..c3ecd6b25 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha2 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index 6613a8896..b315b789e 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha3 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/bundle/backstage.io/manifests/backstage-default-config_v1_configmap.yaml b/bundle/backstage.io/manifests/backstage-default-config_v1_configmap.yaml index 8cdbc58f1..9dbc745b3 100644 --- a/bundle/backstage.io/manifests/backstage-default-config_v1_configmap.yaml +++ b/bundle/backstage.io/manifests/backstage-default-config_v1_configmap.yaml @@ -177,7 +177,7 @@ data: spec: containers: - name: backstage-backend - image: ghcr.io/backstage/backstage:1.28.4 + image: ghcr.io/backstage/backstage:1.38.1 imagePullPolicy: IfNotPresent command: - "node" diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index bd9a5a557..4e3cb062e 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -35,7 +35,7 @@ metadata: } } ] - createdAt: "2025-04-29T08:33:43Z" + createdAt: "2025-05-01T19:54:51Z" description: Backstage Operator operators.operatorframework.io/builder: operator-sdk-v1.37.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index 4f1fff553..77cc35cac 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.7 - createdAt: "2025-04-29T08:33:45Z" + createdAt: "2025-05-01T19:54:52Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed @@ -266,7 +266,7 @@ spec: value: quay.io/fedora/postgresql-15:latest - name: RELATED_IMAGE_backstage value: quay.io/rhdh/rhdh-hub-rhel9:next - image: quay.io/rhdh/rhdh-rhel9-operator:1.7 + image: quay.io/gazarenk/backstage-operator:latest livenessProbe: httpGet: path: /healthz diff --git a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml index 041692c35..1ff9ecbe2 100644 --- a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml @@ -382,7 +382,7 @@ data: includes: - dynamic-plugins.default.yaml plugins: - - disabled: false + - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz" integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== pluginConfig: @@ -400,7 +400,7 @@ data: text: Orchestrator module: OrchestratorPlugin path: /orchestrator - - disabled: false + - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-backend-dynamic-1.5.1.tgz" integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g== pluginConfig: @@ -409,7 +409,7 @@ data: url: http://sonataflow-platform-data-index-service dependencies: - ref: orchestrator - - disabled: false + - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== pluginConfig: diff --git a/bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml index 1ba94f0c5..65f2352fa 100644 --- a/bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml @@ -1,136 +1,5 @@ apiVersion: v1 data: - sample-greetings.yaml: |+ - apiVersion: v1 - data: - greeting.sw.input-schema.json: | - { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "language": { - "title": "Language", - "description": "Language to greet", - "type": "string", - "enum": ["English", "Spanish"], - "default": "English" - } - } - } - workflow-output-schema.json: |- - { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "WorkflowResult", - "description": "Schema of workflow output", - "type": "object", - "properties": { - "result": { - "$ref": "../shared/schemas/workflow-result-schema.json", - "type": "object" - } - } - } - kind: ConfigMap - metadata: - name: 01-greeting-resources-schemas - --- - apiVersion: sonataflow.org/v1alpha08 - kind: SonataFlow - metadata: - annotations: - sonataflow.org/description: YAML based greeting workflow - sonataflow.org/expressionLang: jq - sonataflow.org/profile: gitops - sonataflow.org/version: "1.0" - labels: - app: greeting - sonataflow.org/workflow-app: greeting - name: greeting - spec: - flow: - annotations: - - workflow-type/infrastructure - dataInputSchema: - failOnValidationErrors: true - schema: schemas/greeting.sw.input-schema.json - functions: - - name: greetFunction - operation: sysout - type: custom - - name: successResult - operation: '{ "result": { "message": "Greeting workflow completed successfully", "outputs":[ { "key":"Selected language", "value": .language }, { "key":"Greeting message", "value": .greeting } ] } }' - type: expression - start: - stateName: ChooseOnLanguage - states: - - dataConditions: - - condition: .language == "English" - transition: - nextState: GreetInEnglish - - condition: .language == "Spanish" - transition: - nextState: GreetInSpanish - defaultCondition: - transition: - nextState: GreetInEnglish - name: ChooseOnLanguage - type: switch - - data: - greeting: Hello from YAML Workflow - name: GreetInEnglish - transition: - nextState: GreetPerson - type: inject - - data: - greeting: Saludos desde YAML Workflow - name: GreetInSpanish - transition: - nextState: GreetPerson - type: inject - - actionMode: sequential - actions: - - actionDataFilter: - useResults: true - functionRef: - arguments: - message: .greeting - invoke: sync - refName: greetFunction - name: greetAction - - actionDataFilter: - useResults: true - functionRef: - invoke: sync - refName: successResult - name: setOutput - end: - terminate: true - name: GreetPerson - type: operation - podTemplate: - container: - resources: {} - image: quay.io/orchestrator/serverless-workflow-greeting:96d772297d3ee964ab9775d7aae7dbbd4f070dd7 - resources: - configMaps: - - configMap: - name: 01-greeting-resources-schemas - workflowPath: schemas - persistence: - postgresql: - secretRef: - name: backstage-psql-secret-{{backstage-name}} - userKey: POSTGRES_USER #hardcoded - passwordKey: POSTGRES_PASSWORD #hardcoded - serviceRef: - name: backstage-psql-{{backstage-name}} # hardcoded backstage-psql-{{cr-name}} - namespace: {{backstage-ns}} # hardcoded {{cr-namespace}} - databaseName: backstage_plugin_orchestrator #hardcoded - port: 5432 - databaseSchema: greeting - status: - address: {} - sonataflow.yaml: | apiVersion: networking.k8s.io/v1 kind: NetworkPolicy diff --git a/dist/backstage.io/install.yaml b/dist/backstage.io/install.yaml index 12a1f4aff..fa305a2a8 100644 --- a/dist/backstage.io/install.yaml +++ b/dist/backstage.io/install.yaml @@ -1688,7 +1688,7 @@ data: spec: containers: - name: backstage-backend - image: ghcr.io/backstage/backstage:1.28.4 + image: ghcr.io/backstage/backstage:1.38.1 imagePullPolicy: IfNotPresent command: - "node" diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index fac3e6fc8..90b09aefa 100644 --- a/dist/rhdh/install.yaml +++ b/dist/rhdh/install.yaml @@ -1893,7 +1893,7 @@ data: includes: - dynamic-plugins.default.yaml plugins: - - disabled: false + - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-1.5.1.tgz" integrity: sha512-7VOe+XGTUzrdO/av0DNHbydOjB3Lo+XdCs6fj3JVODLP7Ypd3GXHf/nssYxG5ZYC9F1t9MNeguE2bZOB6ckqTA== pluginConfig: @@ -1911,7 +1911,7 @@ data: text: Orchestrator module: OrchestratorPlugin path: /orchestrator - - disabled: false + - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-orchestrator-backend-dynamic-1.5.1.tgz" integrity: sha512-VIenFStdq9QvvmgmEMG8O7b2wqIebvEcqNeJ9SWZ8jen9t+efTK6D3Rde74LQ1no1QaHLx8RoxNCOuTUEF8O/g== pluginConfig: @@ -1920,7 +1920,7 @@ data: url: http://sonataflow-platform-data-index-service dependencies: - ref: orchestrator - - disabled: false + - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== pluginConfig: @@ -1978,137 +1978,6 @@ metadata: --- apiVersion: v1 data: - sample-greetings.yaml: |+ - apiVersion: v1 - data: - greeting.sw.input-schema.json: | - { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "language": { - "title": "Language", - "description": "Language to greet", - "type": "string", - "enum": ["English", "Spanish"], - "default": "English" - } - } - } - workflow-output-schema.json: |- - { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "WorkflowResult", - "description": "Schema of workflow output", - "type": "object", - "properties": { - "result": { - "$ref": "../shared/schemas/workflow-result-schema.json", - "type": "object" - } - } - } - kind: ConfigMap - metadata: - name: 01-greeting-resources-schemas - --- - apiVersion: sonataflow.org/v1alpha08 - kind: SonataFlow - metadata: - annotations: - sonataflow.org/description: YAML based greeting workflow - sonataflow.org/expressionLang: jq - sonataflow.org/profile: gitops - sonataflow.org/version: "1.0" - labels: - app: greeting - sonataflow.org/workflow-app: greeting - name: greeting - spec: - flow: - annotations: - - workflow-type/infrastructure - dataInputSchema: - failOnValidationErrors: true - schema: schemas/greeting.sw.input-schema.json - functions: - - name: greetFunction - operation: sysout - type: custom - - name: successResult - operation: '{ "result": { "message": "Greeting workflow completed successfully", "outputs":[ { "key":"Selected language", "value": .language }, { "key":"Greeting message", "value": .greeting } ] } }' - type: expression - start: - stateName: ChooseOnLanguage - states: - - dataConditions: - - condition: .language == "English" - transition: - nextState: GreetInEnglish - - condition: .language == "Spanish" - transition: - nextState: GreetInSpanish - defaultCondition: - transition: - nextState: GreetInEnglish - name: ChooseOnLanguage - type: switch - - data: - greeting: Hello from YAML Workflow - name: GreetInEnglish - transition: - nextState: GreetPerson - type: inject - - data: - greeting: Saludos desde YAML Workflow - name: GreetInSpanish - transition: - nextState: GreetPerson - type: inject - - actionMode: sequential - actions: - - actionDataFilter: - useResults: true - functionRef: - arguments: - message: .greeting - invoke: sync - refName: greetFunction - name: greetAction - - actionDataFilter: - useResults: true - functionRef: - invoke: sync - refName: successResult - name: setOutput - end: - terminate: true - name: GreetPerson - type: operation - podTemplate: - container: - resources: {} - image: quay.io/orchestrator/serverless-workflow-greeting:96d772297d3ee964ab9775d7aae7dbbd4f070dd7 - resources: - configMaps: - - configMap: - name: 01-greeting-resources-schemas - workflowPath: schemas - persistence: - postgresql: - secretRef: - name: backstage-psql-secret-{{backstage-name}} - userKey: POSTGRES_USER #hardcoded - passwordKey: POSTGRES_PASSWORD #hardcoded - serviceRef: - name: backstage-psql-{{backstage-name}} # hardcoded backstage-psql-{{cr-name}} - namespace: {{backstage-ns}} # hardcoded {{cr-namespace}} - databaseName: backstage_plugin_orchestrator #hardcoded - port: 5432 - databaseSchema: greeting - status: - address: {} - sonataflow.yaml: | apiVersion: networking.k8s.io/v1 kind: NetworkPolicy From a3159ae787a84b574753f3b0a42353bbc8a49a0f Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Fri, 2 May 2025 14:37:13 +0300 Subject: [PATCH 22/35] simplify profile structure Signed-off-by: gazarenkov --- Makefile | 12 +-- config/manager/deployment.yaml | 7 ++ .../default-config/app-config.yaml | 0 .../default-config/db-secret.yaml | 0 .../default-config/db-service.yaml | 0 .../default-config/db-statefulset.yaml | 0 .../default-config/deployment.yaml | 0 .../{operator => }/default-config/route.yaml | 0 .../default-config/service.yaml | 0 .../profile/backstage.io/kustomization.yaml | 23 +++++- .../{operator => }/namespace.yaml | 0 .../backstage.io/operator/kustomization.yaml | 30 -------- .../default-config/app-config.yaml | 0 .../default-config/configmap-envs.yaml.sample | 0 .../configmap-files.yaml.sample | 0 .../default-config/db-secret.yaml | 0 .../default-config/db-service.yaml | 0 .../default-config/db-statefulset.yaml | 0 .../default-config/db-statefulset.yaml.k8s | 0 .../default-config/deployment.yaml | 0 .../default-config/deployment.yaml.k8s | 0 .../default-config/dynamic-plugins.yaml | 2 +- .../{operator => }/default-config/route.yaml | 0 .../default-config/secret-envs.yaml | 0 .../default-config/secret-files.yaml.sample | 0 .../default-config/service.yaml | 0 .../default-config/service.yaml.k8s | 0 config/profile/rhdh/kustomization.yaml | 42 +++++++++- .../rhdh/{operator => }/namespace.yaml | 0 .../profile/rhdh/operator/kustomization.yaml | 45 ----------- .../patches/deployment-patch.yaml | 14 ++-- .../sonataflow.yaml | 0 docs/dynamic-plugins.md | 77 +++++-------------- examples/orchestrator.yaml | 2 +- pkg/model/plugin_deps.go | 33 ++++---- pkg/model/plugin_deps_test.go | 55 ++++--------- 36 files changed, 134 insertions(+), 208 deletions(-) rename config/profile/backstage.io/{operator => }/default-config/app-config.yaml (100%) rename config/profile/backstage.io/{operator => }/default-config/db-secret.yaml (100%) rename config/profile/backstage.io/{operator => }/default-config/db-service.yaml (100%) rename config/profile/backstage.io/{operator => }/default-config/db-statefulset.yaml (100%) rename config/profile/backstage.io/{operator => }/default-config/deployment.yaml (100%) rename config/profile/backstage.io/{operator => }/default-config/route.yaml (100%) rename config/profile/backstage.io/{operator => }/default-config/service.yaml (100%) rename config/profile/backstage.io/{operator => }/namespace.yaml (100%) delete mode 100644 config/profile/backstage.io/operator/kustomization.yaml rename config/profile/rhdh/{operator => }/default-config/app-config.yaml (100%) rename config/profile/rhdh/{operator => }/default-config/configmap-envs.yaml.sample (100%) rename config/profile/rhdh/{operator => }/default-config/configmap-files.yaml.sample (100%) rename config/profile/rhdh/{operator => }/default-config/db-secret.yaml (100%) rename config/profile/rhdh/{operator => }/default-config/db-service.yaml (100%) rename config/profile/rhdh/{operator => }/default-config/db-statefulset.yaml (100%) rename config/profile/rhdh/{operator => }/default-config/db-statefulset.yaml.k8s (100%) rename config/profile/rhdh/{operator => }/default-config/deployment.yaml (100%) rename config/profile/rhdh/{operator => }/default-config/deployment.yaml.k8s (100%) rename config/profile/rhdh/{operator => }/default-config/dynamic-plugins.yaml (99%) rename config/profile/rhdh/{operator => }/default-config/route.yaml (100%) rename config/profile/rhdh/{operator => }/default-config/secret-envs.yaml (100%) rename config/profile/rhdh/{operator => }/default-config/secret-files.yaml.sample (100%) rename config/profile/rhdh/{operator => }/default-config/service.yaml (100%) rename config/profile/rhdh/{operator => }/default-config/service.yaml.k8s (100%) rename config/profile/rhdh/{operator => }/namespace.yaml (100%) delete mode 100644 config/profile/rhdh/operator/kustomization.yaml rename config/profile/rhdh/{operator => }/patches/deployment-patch.yaml (74%) rename config/profile/rhdh/{operator/plugin-deps/orchestrator => plugin-deps}/sonataflow.yaml (100%) diff --git a/Makefile b/Makefile index de3a68c70..c66d4a6b1 100644 --- a/Makefile +++ b/Makefile @@ -161,14 +161,14 @@ fmt: goimports ## Format the code using goimports .PHONY: test test: manifests generate fmt vet envtest $(LOCALBIN) ## Run tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path - mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/operator/default-config/* $(LOCALBIN)/default-config - mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps 2>/dev/null || : + mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config + mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/plugin-deps/* $(LOCALBIN)/plugin-deps 2>/dev/null || : LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(PKGS) -coverprofile cover.out .PHONY: integration-test integration-test: ginkgo manifests generate fmt vet envtest $(LOCALBIN) ## Run integration_tests. We need LOCALBIN=$(LOCALBIN) to get correct default-config path - mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/operator/default-config/* $(LOCALBIN)/default-config - mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps 2>/dev/null || : + mkdir -p $(LOCALBIN)/default-config && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config + mkdir -p $(LOCALBIN)/plugin-deps && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/plugin-deps/* $(LOCALBIN)/plugin-deps 2>/dev/null || : LOCALBIN=$(LOCALBIN) KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" $(GINKGO) -v -r $(ARGS) integration_tests # After this time, Ginkgo will emit progress reports, so we can get visibility into long-running tests. @@ -220,8 +220,8 @@ build: manifests generate fmt vet ## Build manager binary. .PHONY: run run: manifests generate fmt vet $(LOCALBIN) ## Run a controller from your host. - mkdir -p $(LOCALBIN)/default-config/ && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/operator/default-config/* $(LOCALBIN)/default-config/ - mkdir -p $(LOCALBIN)/plugin-deps/ && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/operator/plugin-deps/* $(LOCALBIN)/plugin-deps/ 2>/dev/null || : + mkdir -p $(LOCALBIN)/default-config/ && rm -fr $(LOCALBIN)/default-config/* && cp -r config/profile/$(PROFILE)/default-config/* $(LOCALBIN)/default-config/ + mkdir -p $(LOCALBIN)/plugin-deps/ && rm -fr $(LOCALBIN)/plugin-deps/* && cp -r config/profile/$(PROFILE)/plugin-deps/* $(LOCALBIN)/plugin-deps/ 2>/dev/null || : go run -C $(LOCALBIN) ../cmd/main.go # by default images expire from quay registry after 14 days diff --git a/config/manager/deployment.yaml b/config/manager/deployment.yaml index 03d47efb7..f273f3bdc 100644 --- a/config/manager/deployment.yaml +++ b/config/manager/deployment.yaml @@ -95,9 +95,16 @@ spec: volumeMounts: - mountPath: /default-config name: default-config + - mountPath: /plugin-deps + name: plugin-deps + serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 volumes: - name: default-config configMap: name: default-config + - name: plugin-deps + configMap: + name: plugin-deps + optional: true \ No newline at end of file diff --git a/config/profile/backstage.io/operator/default-config/app-config.yaml b/config/profile/backstage.io/default-config/app-config.yaml similarity index 100% rename from config/profile/backstage.io/operator/default-config/app-config.yaml rename to config/profile/backstage.io/default-config/app-config.yaml diff --git a/config/profile/backstage.io/operator/default-config/db-secret.yaml b/config/profile/backstage.io/default-config/db-secret.yaml similarity index 100% rename from config/profile/backstage.io/operator/default-config/db-secret.yaml rename to config/profile/backstage.io/default-config/db-secret.yaml diff --git a/config/profile/backstage.io/operator/default-config/db-service.yaml b/config/profile/backstage.io/default-config/db-service.yaml similarity index 100% rename from config/profile/backstage.io/operator/default-config/db-service.yaml rename to config/profile/backstage.io/default-config/db-service.yaml diff --git a/config/profile/backstage.io/operator/default-config/db-statefulset.yaml b/config/profile/backstage.io/default-config/db-statefulset.yaml similarity index 100% rename from config/profile/backstage.io/operator/default-config/db-statefulset.yaml rename to config/profile/backstage.io/default-config/db-statefulset.yaml diff --git a/config/profile/backstage.io/operator/default-config/deployment.yaml b/config/profile/backstage.io/default-config/deployment.yaml similarity index 100% rename from config/profile/backstage.io/operator/default-config/deployment.yaml rename to config/profile/backstage.io/default-config/deployment.yaml diff --git a/config/profile/backstage.io/operator/default-config/route.yaml b/config/profile/backstage.io/default-config/route.yaml similarity index 100% rename from config/profile/backstage.io/operator/default-config/route.yaml rename to config/profile/backstage.io/default-config/route.yaml diff --git a/config/profile/backstage.io/operator/default-config/service.yaml b/config/profile/backstage.io/default-config/service.yaml similarity index 100% rename from config/profile/backstage.io/operator/default-config/service.yaml rename to config/profile/backstage.io/default-config/service.yaml diff --git a/config/profile/backstage.io/kustomization.yaml b/config/profile/backstage.io/kustomization.yaml index cb55fb7e1..5f6ab1283 100644 --- a/config/profile/backstage.io/kustomization.yaml +++ b/config/profile/backstage.io/kustomization.yaml @@ -1,9 +1,30 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization +namespace: backstage-system + +namePrefix: backstage- + resources: -- operator +- ../../crd +- ../../rbac +- ../../manager +- namespace.yaml + images: - name: controller newName: quay.io/rhdh-community/operator newTag: 0.7.0 + +generatorOptions: + disableNameSuffixHash: true + +configMapGenerator: +- files: + - default-config/app-config.yaml + - default-config/db-secret.yaml + - default-config/db-service.yaml + - default-config/db-statefulset.yaml + - default-config/deployment.yaml + - default-config/service.yaml + name: default-config diff --git a/config/profile/backstage.io/operator/namespace.yaml b/config/profile/backstage.io/namespace.yaml similarity index 100% rename from config/profile/backstage.io/operator/namespace.yaml rename to config/profile/backstage.io/namespace.yaml diff --git a/config/profile/backstage.io/operator/kustomization.yaml b/config/profile/backstage.io/operator/kustomization.yaml deleted file mode 100644 index e38c41f7f..000000000 --- a/config/profile/backstage.io/operator/kustomization.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -namespace: backstage-system - -namePrefix: backstage- - -resources: -- ../../../crd -- ../../../rbac -- ../../../manager -- namespace.yaml - -images: -- name: controller - newName: quay.io/rhdh-community/operator - newTag: 0.7.0 - -generatorOptions: - disableNameSuffixHash: true - -configMapGenerator: -- files: - - default-config/app-config.yaml - - default-config/db-secret.yaml - - default-config/db-service.yaml - - default-config/db-statefulset.yaml - - default-config/deployment.yaml - - default-config/service.yaml - name: default-config diff --git a/config/profile/rhdh/operator/default-config/app-config.yaml b/config/profile/rhdh/default-config/app-config.yaml similarity index 100% rename from config/profile/rhdh/operator/default-config/app-config.yaml rename to config/profile/rhdh/default-config/app-config.yaml diff --git a/config/profile/rhdh/operator/default-config/configmap-envs.yaml.sample b/config/profile/rhdh/default-config/configmap-envs.yaml.sample similarity index 100% rename from config/profile/rhdh/operator/default-config/configmap-envs.yaml.sample rename to config/profile/rhdh/default-config/configmap-envs.yaml.sample diff --git a/config/profile/rhdh/operator/default-config/configmap-files.yaml.sample b/config/profile/rhdh/default-config/configmap-files.yaml.sample similarity index 100% rename from config/profile/rhdh/operator/default-config/configmap-files.yaml.sample rename to config/profile/rhdh/default-config/configmap-files.yaml.sample diff --git a/config/profile/rhdh/operator/default-config/db-secret.yaml b/config/profile/rhdh/default-config/db-secret.yaml similarity index 100% rename from config/profile/rhdh/operator/default-config/db-secret.yaml rename to config/profile/rhdh/default-config/db-secret.yaml diff --git a/config/profile/rhdh/operator/default-config/db-service.yaml b/config/profile/rhdh/default-config/db-service.yaml similarity index 100% rename from config/profile/rhdh/operator/default-config/db-service.yaml rename to config/profile/rhdh/default-config/db-service.yaml diff --git a/config/profile/rhdh/operator/default-config/db-statefulset.yaml b/config/profile/rhdh/default-config/db-statefulset.yaml similarity index 100% rename from config/profile/rhdh/operator/default-config/db-statefulset.yaml rename to config/profile/rhdh/default-config/db-statefulset.yaml diff --git a/config/profile/rhdh/operator/default-config/db-statefulset.yaml.k8s b/config/profile/rhdh/default-config/db-statefulset.yaml.k8s similarity index 100% rename from config/profile/rhdh/operator/default-config/db-statefulset.yaml.k8s rename to config/profile/rhdh/default-config/db-statefulset.yaml.k8s diff --git a/config/profile/rhdh/operator/default-config/deployment.yaml b/config/profile/rhdh/default-config/deployment.yaml similarity index 100% rename from config/profile/rhdh/operator/default-config/deployment.yaml rename to config/profile/rhdh/default-config/deployment.yaml diff --git a/config/profile/rhdh/operator/default-config/deployment.yaml.k8s b/config/profile/rhdh/default-config/deployment.yaml.k8s similarity index 100% rename from config/profile/rhdh/operator/default-config/deployment.yaml.k8s rename to config/profile/rhdh/default-config/deployment.yaml.k8s diff --git a/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml b/config/profile/rhdh/default-config/dynamic-plugins.yaml similarity index 99% rename from config/profile/rhdh/operator/default-config/dynamic-plugins.yaml rename to config/profile/rhdh/default-config/dynamic-plugins.yaml index c761a798d..87770acb9 100644 --- a/config/profile/rhdh/operator/default-config/dynamic-plugins.yaml +++ b/config/profile/rhdh/default-config/dynamic-plugins.yaml @@ -55,7 +55,7 @@ data: dataIndexService: url: http://sonataflow-platform-data-index-service dependencies: - - ref: orchestrator + - ref: sonataflow - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== diff --git a/config/profile/rhdh/operator/default-config/route.yaml b/config/profile/rhdh/default-config/route.yaml similarity index 100% rename from config/profile/rhdh/operator/default-config/route.yaml rename to config/profile/rhdh/default-config/route.yaml diff --git a/config/profile/rhdh/operator/default-config/secret-envs.yaml b/config/profile/rhdh/default-config/secret-envs.yaml similarity index 100% rename from config/profile/rhdh/operator/default-config/secret-envs.yaml rename to config/profile/rhdh/default-config/secret-envs.yaml diff --git a/config/profile/rhdh/operator/default-config/secret-files.yaml.sample b/config/profile/rhdh/default-config/secret-files.yaml.sample similarity index 100% rename from config/profile/rhdh/operator/default-config/secret-files.yaml.sample rename to config/profile/rhdh/default-config/secret-files.yaml.sample diff --git a/config/profile/rhdh/operator/default-config/service.yaml b/config/profile/rhdh/default-config/service.yaml similarity index 100% rename from config/profile/rhdh/operator/default-config/service.yaml rename to config/profile/rhdh/default-config/service.yaml diff --git a/config/profile/rhdh/operator/default-config/service.yaml.k8s b/config/profile/rhdh/default-config/service.yaml.k8s similarity index 100% rename from config/profile/rhdh/operator/default-config/service.yaml.k8s rename to config/profile/rhdh/default-config/service.yaml.k8s diff --git a/config/profile/rhdh/kustomization.yaml b/config/profile/rhdh/kustomization.yaml index 67253cc6d..624b047f9 100644 --- a/config/profile/rhdh/kustomization.yaml +++ b/config/profile/rhdh/kustomization.yaml @@ -1,9 +1,45 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization +namespace: rhdh-operator + +namePrefix: rhdh- + resources: -- operator +- ../../crd +- ../../rbac +- ../../manager +- namespace.yaml + + images: - name: controller - newName: quay.io/rhdh/rhdh-rhel9-operator - newTag: "1.7" + newName: quay.io/gazarenk/backstage-operator + +patches: +- path: patches/deployment-patch.yaml + target: + kind: Deployment + name: operator + +generatorOptions: + disableNameSuffixHash: true + +configMapGenerator: +- files: + - default-config/app-config.yaml + - default-config/db-secret.yaml + - default-config/db-service.yaml + - default-config/db-statefulset.yaml + - default-config/deployment.yaml + - default-config/dynamic-plugins.yaml + - default-config/route.yaml + - default-config/secret-envs.yaml + - default-config/service.yaml + - default-config/deployment.yaml.k8s + - default-config/db-statefulset.yaml.k8s + - default-config/service.yaml.k8s + name: default-config +- files: + - plugin-deps/sonataflow.yaml + name: plugin-deps diff --git a/config/profile/rhdh/operator/namespace.yaml b/config/profile/rhdh/namespace.yaml similarity index 100% rename from config/profile/rhdh/operator/namespace.yaml rename to config/profile/rhdh/namespace.yaml diff --git a/config/profile/rhdh/operator/kustomization.yaml b/config/profile/rhdh/operator/kustomization.yaml deleted file mode 100644 index 0b4dc4bb6..000000000 --- a/config/profile/rhdh/operator/kustomization.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -namespace: rhdh-operator - -namePrefix: rhdh- - -resources: -- ../../../crd -- ../../../rbac -- ../../../manager -- namespace.yaml - - -images: -- name: controller - newName: quay.io/gazarenk/backstage-operator - -patches: -- path: patches/deployment-patch.yaml - target: - kind: Deployment - name: operator - -generatorOptions: - disableNameSuffixHash: true - -configMapGenerator: - - files: - - default-config/app-config.yaml - - default-config/db-secret.yaml - - default-config/db-service.yaml - - default-config/db-statefulset.yaml - - default-config/deployment.yaml - - default-config/dynamic-plugins.yaml - - default-config/route.yaml - - default-config/secret-envs.yaml - - default-config/service.yaml - - default-config/deployment.yaml.k8s - - default-config/db-statefulset.yaml.k8s - - default-config/service.yaml.k8s - name: default-config - - files: - - plugin-deps/orchestrator/sonataflow.yaml - name: plugin-deps-orchestrator diff --git a/config/profile/rhdh/operator/patches/deployment-patch.yaml b/config/profile/rhdh/patches/deployment-patch.yaml similarity index 74% rename from config/profile/rhdh/operator/patches/deployment-patch.yaml rename to config/profile/rhdh/patches/deployment-patch.yaml index b737681d6..904d502d0 100644 --- a/config/profile/rhdh/operator/patches/deployment-patch.yaml +++ b/config/profile/rhdh/patches/deployment-patch.yaml @@ -27,10 +27,10 @@ spec: value: quay.io/fedora/postgresql-15:latest - name: RELATED_IMAGE_backstage value: quay.io/rhdh/rhdh-hub-rhel9:next - volumeMounts: - - name: plugin-deps-orchestrator - mountPath: plugin-deps/orchestrator - volumes: - - name: plugin-deps-orchestrator - configMap: - name: rhdh-plugin-deps-orchestrator +# volumeMounts: +# - name: plugin-deps-orchestrator +# mountPath: plugin-deps/orchestrator +# volumes: +# - name: plugin-deps-orchestrator +# configMap: +# name: rhdh-plugin-deps-orchestrator diff --git a/config/profile/rhdh/operator/plugin-deps/orchestrator/sonataflow.yaml b/config/profile/rhdh/plugin-deps/sonataflow.yaml similarity index 100% rename from config/profile/rhdh/operator/plugin-deps/orchestrator/sonataflow.yaml rename to config/profile/rhdh/plugin-deps/sonataflow.yaml diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index e2133f404..0127eb403 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -4,85 +4,46 @@ Dynamic plugins configured for the Backstage CR may require certain Kubernetes resources to be configured to make the plugin work. These are referred to as 'plugin dependencies'. Starting from version 1.7, it is possible to automatically create these resources when the Backstage CR is applied to the cluster. ### Profile Configuration -Plugin dependency configuration for a specific profile is done via the `/config/profile/{PROFILE}/operator/plugin-deps` directory. To enable this, the administrator should: -- Create a directory with the name associated with the plugin (for convenience) and place the required resources as Kubernetes manifests in YAML format within it. -- Create/modify kustomization.yaml to generate a ConfigMap with these files. -- Create/modify a patch which make Kustomize mount this ConfigMap to the Backstage container. +Plugin dependency configuration for a specific profile is done via the `/config/profile/{PROFILE}/plugin-deps` directory. To enable this, the administrator should place the required resources as Kubernetes manifests in YAML format within **plugin-deps** directory. **Example Directory Structure**: ```txt config/ profile/ rhdh/ - operator/ - kustomization.yaml - example-plugin-patch.yaml - plugin-deps/ - example/ - dep1.yaml - dep2.yaml + kustomization.yaml + plugin-deps/ + example-dep1.yaml + example-dep2.yaml ``` -Here, **dep1.yaml** and **dep2.yaml** are the plugin dependencies for the example plugin. +Here, **example-dep1.yaml** and **example-dep2.yaml** are the plugin dependencies for the example plugin. **Notes:** * If a resource manifest does not specify a namespace, it will be created in the namespace of the Backstage CR. -* Resources may contain {{backstage-name}} and {{backstage-ns}} placeholders, which will be replaced with the name and namespace of the Backstage CR, respectively. +* Resources may contain **{{backstage-name}}** and **{{backstage-ns}}** placeholders, which will be replaced with the name and namespace of the Backstage CR, respectively. The `kustomization.yaml` file should contain the following lines: ```yaml -patches: - - path: example-plugin-patch.yaml - target: - kind: Deployment - name: operator - + configMapGenerator: - files: - - plugin-deps/example/dep1.yaml - - plugin-deps/example/dep2.yaml - name: plugin-deps-example -``` -The names of patch file and configmap are arbitrary. - -The patch file (`example-plugin-patch.yaml` in this example) should contain the following lines: -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: operator -spec: - selector: - matchLabels: - app: backstage-operator - template: - spec: - containers: - - name: manager - volumeMounts: - - name: plugin-deps-example - mountPath: plugin-deps/example - volumes: - - name: plugin-deps-example - configMap: - name: plugin-deps-example + - plugin-deps/example-dep1.yaml + - plugin-deps/example-dep2.yaml + name: plugin-deps ``` -Ensure that: -* **spec.template.spec.volumes.configMap.name** matches configMapGenerator.name in the kustomization.yaml file. -* **spec.template.spec.containers.volumeMounts.mountPath** matches the directory path in plugin-deps/example. - ### Plugin dependencies infrastructure -If plugin dependencies require infrastructural resources (e.g., RoleBindings, CustomResources) and if the User (Administrator) wants it to be deployed (see Note below), they can be specified in the /config/profile/{PROFILE}/plugin-infra directory. For convenience, these can be grouped per plugin in subdirectories. To create these resources (along with the operator deployment), use the make plugin-infra command. +If plugin dependencies require infrastructural resources (e.g., RoleBindings, CustomResources) and if the User (Administrator) wants it to be deployed (see Note below), they can be specified in the /config/profile/{PROFILE}/plugin-infra directory. To create these resources (along with the operator deployment), use the `make plugin-infra` command. **Note**: Be cautious when running this command on a production cluster, as it may reconfigure cluster-scoped resources. ### Plugin configuration -To create the plugin dependencies when the Backstage CR is applied, they must be referenced in the **dependencies** field of the plugin configuration. The operator will look for the plugin-deps directory in the default-config directory of the profile and create the resources described in the files within this directory. +To create the plugin dependencies when the Backstage CR is applied, they must be referenced in the **dependencies** field of the plugin configuration. The operator will look for the **plugin-deps** directory and create the resources described in the files within this directory. -Plugin dependencies can be referenced in the dynamic-plugins' ConfigMap. This can either be part of the profile's [default configuration](configuration.md/#default-configuration-files) for all Backstage CRs or part of the [ConfigMap referenced in the Backstage CR](configuration.md/#dynamic-plugins). Starting from version 1.7, plugin dependencies can be included in the dynamic plugin configuration as follows: +Plugin dependencies can be referenced in the dynamic-plugins' ConfigMap. This can either be part of the profile's [default configuration](configuration.md/#default-configuration-files) for all Backstage CRs or part of the [ConfigMap referenced in the Backstage CR](configuration.md/#dynamic-plugins). Starting from version 1.7, plugin dependencies can be included in the dynamic plugin configuration. Each `dependencies.ref` value can either match the full file name or serve as a prefix for the file name. The operator will look for files in the `plugin-deps` directory whose names either start with the specified `ref` value or exactly match it. These files will be used to create the resources described within them. ```yaml apiVersion: v1 @@ -97,12 +58,10 @@ data: - disabled: false package: "path-or-url-to-example-plugin" dependencies: - - ref: example + - ref: example-dep ``` -* In this example, the example dependency is referenced. -* The operator will look for the plugin-deps/example directory in the profile and create the resources described in the files within this directory. -This ensures that all required resources for the plugin are automatically created when the Backstage CR is applied. +In this example, both example-dep1.yaml and example-dep1.yaml will be picked and operator create the resources described in the files. ### Example: Orchestrator plugin dependencies The orchestrator plugin (as of v1.5.1) consists of three dynamic plugins: @@ -116,9 +75,9 @@ The orchestrator plugin has the following dependencies: - A Sonataflowplatform custom resource - created in the namespace of the Backstage CR. - Knativeeventing and Knativeserving custom resources to be created in the knative-eventing and knative-serving namespaces respectively. - A set of NetworkPolicies to communicate to Knative resources created in the namespace of Backstage CR. -See [profile/rhdh/operator/plugin-deps/orchestrator](/config/profile/rhdh/operator/plugin-deps/orchestrator) +See [profile/rhdh/plugin-deps](/config/profile/rhdh/plugin-deps) -The orchestrator-backend plugin uses the service sonataflow-platform-data-index-service, which is created by the SonataFlowPlatform CR. This service is used to communicate with the SonataFlow platform. +The orchestrator-backend plugin uses the service **sonataflow-platform-data-index-service**, which is created by the SonataFlowPlatform CR. This service is used to communicate with the SonataFlow platform. **Important:** The sonataflowplatform CR contains dataIndex service that requires PostgreSQL database. diff --git a/examples/orchestrator.yaml b/examples/orchestrator.yaml index 4e60ef207..835189b96 100644 --- a/examples/orchestrator.yaml +++ b/examples/orchestrator.yaml @@ -33,7 +33,7 @@ data: dataIndexService: url: http://sonataflow-platform-data-index-service dependencies: - - ref: orchestrator + - ref: sonataflow - disabled: false package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== diff --git a/pkg/model/plugin_deps.go b/pkg/model/plugin_deps.go index 04bdfba4e..c0fd31967 100644 --- a/pkg/model/plugin_deps.go +++ b/pkg/model/plugin_deps.go @@ -18,7 +18,7 @@ func GetPluginDeps(bsName, bsNamespace string, plugins DynamicPlugins) ([]*unstr return nil, fmt.Errorf("failed to get plugin dependencies: %w", err) } - //get refs from enabledDirs + //get refs from enabled var refs []string for _, dep := range pdeps { if dep.Ref != "" { @@ -32,7 +32,7 @@ func GetPluginDeps(bsName, bsNamespace string, plugins DynamicPlugins) ([]*unstr // ReadPluginDeps reads the plugin dependencies from the specified directory // and returns a slice of unstructured.Unstructured objects. -func ReadPluginDeps(rootDir, bsName, bsNamespace string, enabledDirs []string) ([]*unstructured.Unstructured, error) { +func ReadPluginDeps(rootDir, bsName, bsNamespace string, enabled []string) ([]*unstructured.Unstructured, error) { if !utils.DirectoryExists(rootDir) { return []*unstructured.Unstructured{}, nil @@ -41,7 +41,7 @@ func ReadPluginDeps(rootDir, bsName, bsNamespace string, enabledDirs []string) ( var objects []*unstructured.Unstructured // Read the directory tree - files, err := getDepsFiles(rootDir, enabledDirs) + files, err := getDepsFiles(rootDir, enabled) if err != nil { return nil, err @@ -74,23 +74,26 @@ func ReadPluginDeps(rootDir, bsName, bsNamespace string, enabledDirs []string) ( return objects, nil } -func getDepsFiles(root string, enabledDirs []string) ([]string, error) { +func getDepsFiles(root string, enabledPrefixes []string) ([]string, error) { var files []string - // Iterate over the specified directories - for _, dir := range enabledDirs { - dirPath := filepath.Join(root, dir) + // Read the directory contents + entries, err := os.ReadDir(root) + if err != nil { + return nil, fmt.Errorf("failed to read directory %s: %w", root, err) + } - // Read the directory contents - entries, err := os.ReadDir(dirPath) - if err != nil { - return nil, fmt.Errorf("failed to read directory %s: %w", dirPath, err) + // Iterate over the entries and filter by prefixes + for _, entry := range entries { + if entry.IsDir() { + continue // Skip directories } - // Collect only files from the first level - for _, entry := range entries { - if !entry.IsDir() { // Skip subdirectories - files = append(files, filepath.Join(dirPath, entry.Name())) + // Check if the file name starts with any of the enabled prefixes + for _, prefix := range enabledPrefixes { + if strings.HasPrefix(entry.Name(), prefix) { + files = append(files, filepath.Join(root, entry.Name())) + break } } } diff --git a/pkg/model/plugin_deps_test.go b/pkg/model/plugin_deps_test.go index 0c5f5cae7..4090dbe87 100644 --- a/pkg/model/plugin_deps_test.go +++ b/pkg/model/plugin_deps_test.go @@ -9,62 +9,37 @@ import ( ) func TestReadPluginDeps(t *testing.T) { - dir := t.TempDir() - // Create subdirectories and files - // subdir1/ - // file0.yaml - // file1.yaml - // subdir11/ - second dir level to always ignore - // file11.yaml - // subdir2/ - // file2.yaml - subdir1 := filepath.Join(dir, "subdir1") - subdir11 := filepath.Join(subdir1, "subdir11") - subdir2 := filepath.Join(dir, "subdir2") - err := os.MkdirAll(subdir1, 0755) - assert.NoError(t, err) - err = os.MkdirAll(subdir2, 0755) - assert.NoError(t, err) - err = os.MkdirAll(subdir11, 0755) - assert.NoError(t, err) + // Create files in the root directory + file1 := filepath.Join(dir, "sonata.yaml") + file2 := filepath.Join(dir, "otherplugin.yaml") + file3 := filepath.Join(dir, "sonata-config.yaml") + file4 := filepath.Join(dir, "unrelated.txt") - file0 := filepath.Join(subdir1, "file0.yaml") - file1 := filepath.Join(subdir1, "file1.yaml") - file11 := filepath.Join(subdir11, "file11.yaml") - file2 := filepath.Join(subdir2, "file2.yaml") - err = os.WriteFile(file1, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test1"), 0644) + err := os.WriteFile(file1, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: sonata"), 0644) assert.NoError(t, err) err = os.WriteFile(file2, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test2"), 0644) assert.NoError(t, err) - err = os.WriteFile(file11, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test11"), 0644) + err = os.WriteFile(file3, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: sonata"), 0644) assert.NoError(t, err) - err = os.WriteFile(file0, []byte("apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: test0"), 0644) + err = os.WriteFile(file4, []byte("some unrelated content"), 0644) assert.NoError(t, err) - // Call ReadPluginDeps for subdir1 - objects, err := ReadPluginDeps(dir, "", "", []string{"subdir1"}) + objects, err := ReadPluginDeps(dir, "", "", []string{"sonata"}) assert.NoError(t, err) assert.Len(t, objects, 2) - assert.Equal(t, "test0", objects[0].GetName()) - assert.Equal(t, "test1", objects[1].GetName()) - objects, err = ReadPluginDeps(dir, "", "", []string{""}) - assert.NoError(t, err) - assert.Len(t, objects, 0) + // Verify the names of the objects + assert.Equal(t, "sonata", objects[0].GetName()) + assert.Equal(t, "sonata", objects[1].GetName()) } func TestReadPluginDepsSubstitutions(t *testing.T) { dir := t.TempDir() - // Create subdirectory and a YAML file with placeholders - subdir1 := filepath.Join(dir, "subdir1") - err := os.MkdirAll(subdir1, 0755) - assert.NoError(t, err) - - file1 := filepath.Join(subdir1, "file1.yaml") + file1 := filepath.Join(dir, "file1.yaml") yamlContent := ` apiVersion: v1 kind: ConfigMap @@ -72,13 +47,13 @@ metadata: name: {{backstage-name}} namespace: {{backstage-ns}} ` - err = os.WriteFile(file1, []byte(yamlContent), 0644) + err := os.WriteFile(file1, []byte(yamlContent), 0644) assert.NoError(t, err) // Call ReadPluginDeps with substitution values bsName := "test-name" bsNamespace := "test-namespace" - objects, err := ReadPluginDeps(dir, bsName, bsNamespace, []string{"subdir1"}) + objects, err := ReadPluginDeps(dir, bsName, bsNamespace, []string{"file1"}) assert.NoError(t, err) assert.Len(t, objects, 1) From 3f521615a2b170d26c65b1d5951c88e47e33f040 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 May 2025 11:38:56 +0000 Subject: [PATCH 23/35] Regenerate bundle/installer manifests Co-authored-by: gazarenkov --- ...kstage-operator.clusterserviceversion.yaml | 8 +- ...kstage-operator.clusterserviceversion.yaml | 15 +- .../rhdh-default-config_v1_configmap.yaml | 2 +- .../rhdh-plugin-deps_v1_configmap.yaml | 130 ++++++++++++++++++ config/profile/rhdh/kustomization.yaml | 3 +- dist/backstage.io/install.yaml | 6 + dist/rhdh/install.yaml | 17 +-- 7 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index 4e3cb062e..c00b935bf 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -35,7 +35,7 @@ metadata: } } ] - createdAt: "2025-05-01T19:54:51Z" + createdAt: "2025-05-02T11:38:50Z" description: Backstage Operator operators.operatorframework.io/builder: operator-sdk-v1.37.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 @@ -250,6 +250,8 @@ spec: volumeMounts: - mountPath: /default-config name: default-config + - mountPath: /plugin-deps + name: plugin-deps securityContext: runAsNonRoot: true serviceAccountName: backstage-controller-manager @@ -258,6 +260,10 @@ spec: - configMap: name: backstage-default-config name: default-config + - configMap: + name: plugin-deps + optional: true + name: plugin-deps permissions: - rules: - apiGroups: diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index 77cc35cac..dbcaf35ba 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.7 - createdAt: "2025-05-01T19:54:52Z" + createdAt: "2025-05-02T11:38:51Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed @@ -266,7 +266,7 @@ spec: value: quay.io/fedora/postgresql-15:latest - name: RELATED_IMAGE_backstage value: quay.io/rhdh/rhdh-hub-rhel9:next - image: quay.io/gazarenk/backstage-operator:latest + image: quay.io/rhdh/rhdh-rhel9-operator:1.7 livenessProbe: httpGet: path: /healthz @@ -300,21 +300,22 @@ spec: - ALL readOnlyRootFilesystem: true volumeMounts: - - mountPath: plugin-deps/orchestrator - name: plugin-deps-orchestrator - mountPath: /default-config name: default-config + - mountPath: /plugin-deps + name: plugin-deps securityContext: runAsNonRoot: true serviceAccountName: rhdh-controller-manager terminationGracePeriodSeconds: 10 volumes: - - configMap: - name: rhdh-plugin-deps-orchestrator - name: plugin-deps-orchestrator - configMap: name: rhdh-default-config name: default-config + - configMap: + name: rhdh-plugin-deps + optional: true + name: plugin-deps permissions: - rules: - apiGroups: diff --git a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml index 1ff9ecbe2..0abbddac9 100644 --- a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml @@ -408,7 +408,7 @@ data: dataIndexService: url: http://sonataflow-platform-data-index-service dependencies: - - ref: orchestrator + - ref: sonataflow - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== diff --git a/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml new file mode 100644 index 000000000..e1eeaf0f8 --- /dev/null +++ b/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml @@ -0,0 +1,130 @@ +apiVersion: v1 +data: + sonataflow.yaml: | + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-knative-to-sonataflow-and-workflows # hardcoded + spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow knative events to be delivered to workflows. + kubernetes.io/metadata.name: knative-eventing + - namespaceSelector: + matchLabels: + # Allow auxiliary knative function for workflow (such as m2k-save-transformation) + kubernetes.io/metadata.name: knative-serving + --- + # NetworkPolicy to unblock incoming traffic to the namespace + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-external-communication # hardcoded + spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow knative events to be delivered to workflows. + kubernetes.io/metadata.name: openshift-ingress + --- + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-intra-network # hardcoded + spec: + # Apply this policy to all pods in the namespace + podSelector: {} + # Specify policy type as 'Ingress' to control incoming traffic rules + policyTypes: + - Ingress + ingress: + - from: + # Allow ingress from any pod within the same namespace + - podSelector: {} + --- + # NetworkPolicy to allow openshift-user-workload-monitoring pods to access all pods within the workflow's namespace + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: allow-monitoring-to-sonataflow-and-workflows # hardcoded + spec: + # Apply this policy to all pods in the namespace + podSelector: {} + # Specify policy type as 'Ingress' to control incoming traffic rules + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + # Allow openshift-user-workload-monitoring pods to access the workflow. + kubernetes.io/metadata.name: openshift-user-workload-monitoring + --- + apiVersion: operator.knative.dev/v1beta1 + kind: KnativeEventing + metadata: + name: knative-eventing + namespace: knative-eventing + spec: + Registry: {} + --- + apiVersion: operator.knative.dev/v1beta1 + kind: KnativeServing + metadata: + name: knative-serving + namespace: knative-serving + spec: + controller-custom-certs: + name: "" + type: "" + registry: {} + --- + apiVersion: sonataflow.org/v1alpha08 + kind: SonataFlowPlatform + metadata: + name: sonataflow-platform + spec: + monitoring: + enabled: true + build: + template: + resources: + requests: + memory: 64Mi + cpu: 250m + limits: + memory: 1Gi + cpu: 500m + services: + dataIndex: + enabled: true + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: backstage-psql-{{backstage-name}} + namespace: {{backstage-ns}} + databaseName: backstage_plugin_orchestrator + jobService: + enabled: true + persistence: + postgresql: + secretRef: + name: backstage-psql-secret-{{backstage-name}} + userKey: POSTGRES_USER + passwordKey: POSTGRES_PASSWORD + serviceRef: + name: backstage-psql-{{backstage-name}} + namespace: {{backstage-ns}} + databaseName: backstage_plugin_orchestrator +kind: ConfigMap +metadata: + name: rhdh-plugin-deps diff --git a/config/profile/rhdh/kustomization.yaml b/config/profile/rhdh/kustomization.yaml index 624b047f9..c24c144cc 100644 --- a/config/profile/rhdh/kustomization.yaml +++ b/config/profile/rhdh/kustomization.yaml @@ -14,7 +14,8 @@ resources: images: - name: controller - newName: quay.io/gazarenk/backstage-operator + newName: quay.io/rhdh/rhdh-rhel9-operator + newTag: "1.7" patches: - path: patches/deployment-patch.yaml diff --git a/dist/backstage.io/install.yaml b/dist/backstage.io/install.yaml index fa305a2a8..5b5bcc9ce 100644 --- a/dist/backstage.io/install.yaml +++ b/dist/backstage.io/install.yaml @@ -1832,6 +1832,8 @@ spec: volumeMounts: - mountPath: /default-config name: default-config + - mountPath: /plugin-deps + name: plugin-deps securityContext: runAsNonRoot: true serviceAccountName: backstage-controller-manager @@ -1840,3 +1842,7 @@ spec: - configMap: name: backstage-default-config name: default-config + - configMap: + name: plugin-deps + optional: true + name: plugin-deps diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index 90b09aefa..68ef1837b 100644 --- a/dist/rhdh/install.yaml +++ b/dist/rhdh/install.yaml @@ -1919,7 +1919,7 @@ data: dataIndexService: url: http://sonataflow-platform-data-index-service dependencies: - - ref: orchestrator + - ref: sonataflow - disabled: true package: "https://github.com/rhdhorchestrator/orchestrator-plugins-internal-release/releases/download/v1.5.1/backstage-plugin-scaffolder-backend-module-orchestrator-dynamic-1.5.1.tgz" integrity: sha512-bnVQjVsUZ470Vgm2kd5Lo/bVa2fF0q4GufBDc/8oTQsnP3zZJQqKFvFElBTCjY76RqkECydlvZ1UFybSzvockQ== @@ -2105,7 +2105,7 @@ data: databaseName: backstage_plugin_orchestrator kind: ConfigMap metadata: - name: rhdh-plugin-deps-orchestrator + name: rhdh-plugin-deps namespace: rhdh-operator --- apiVersion: v1 @@ -2187,7 +2187,7 @@ spec: value: quay.io/fedora/postgresql-15:latest - name: RELATED_IMAGE_backstage value: quay.io/rhdh/rhdh-hub-rhel9:next - image: quay.io/gazarenk/backstage-operator:latest + image: quay.io/rhdh/rhdh-rhel9-operator:1.7 livenessProbe: httpGet: path: /healthz @@ -2221,18 +2221,19 @@ spec: - ALL readOnlyRootFilesystem: true volumeMounts: - - mountPath: plugin-deps/orchestrator - name: plugin-deps-orchestrator - mountPath: /default-config name: default-config + - mountPath: /plugin-deps + name: plugin-deps securityContext: runAsNonRoot: true serviceAccountName: rhdh-controller-manager terminationGracePeriodSeconds: 10 volumes: - - configMap: - name: rhdh-plugin-deps-orchestrator - name: plugin-deps-orchestrator - configMap: name: rhdh-default-config name: default-config + - configMap: + name: rhdh-plugin-deps + optional: true + name: plugin-deps From 27d7899997cdfacf9aafb0957f3b8db7329cc4e1 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Mon, 5 May 2025 12:32:33 +0300 Subject: [PATCH 24/35] fixes Signed-off-by: gazarenkov --- config/profile/rhdh/kustomization.yaml | 3 ++- config/profile/rhdh/patches/deployment-patch.yaml | 8 +------- .../plugin-infra/orchestrator/infra-serverless.yaml | 2 +- .../plugin-infra/orchestrator/infra-sonataflow.yaml | 2 +- docs/developer.md | 5 +++++ docs/dynamic-plugins.md | 12 ++++++++---- examples/orchestrator.yaml | 4 ---- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/config/profile/rhdh/kustomization.yaml b/config/profile/rhdh/kustomization.yaml index 624b047f9..c24c144cc 100644 --- a/config/profile/rhdh/kustomization.yaml +++ b/config/profile/rhdh/kustomization.yaml @@ -14,7 +14,8 @@ resources: images: - name: controller - newName: quay.io/gazarenk/backstage-operator + newName: quay.io/rhdh/rhdh-rhel9-operator + newTag: "1.7" patches: - path: patches/deployment-patch.yaml diff --git a/config/profile/rhdh/patches/deployment-patch.yaml b/config/profile/rhdh/patches/deployment-patch.yaml index 904d502d0..404a5248a 100644 --- a/config/profile/rhdh/patches/deployment-patch.yaml +++ b/config/profile/rhdh/patches/deployment-patch.yaml @@ -27,10 +27,4 @@ spec: value: quay.io/fedora/postgresql-15:latest - name: RELATED_IMAGE_backstage value: quay.io/rhdh/rhdh-hub-rhel9:next -# volumeMounts: -# - name: plugin-deps-orchestrator -# mountPath: plugin-deps/orchestrator -# volumes: -# - name: plugin-deps-orchestrator -# configMap: -# name: rhdh-plugin-deps-orchestrator + diff --git a/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml b/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml index dc86013e4..085fe8036 100644 --- a/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml +++ b/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml @@ -17,7 +17,7 @@ metadata: namespace: openshift-serverless spec: channel: stable # channel of an operator package to subscribe to - installPlanApproval: Automatic #Manual # whether the update should be installed automatically + installPlanApproval: Automatic # whether the update should be installed automatically name: serverless-operator # name of the operator package source: redhat-operators # name of the catalog source sourceNamespace: openshift-marketplace diff --git a/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml b/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml index 7e8e44f1c..ed686bd8d 100644 --- a/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml +++ b/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml @@ -17,7 +17,7 @@ metadata: namespace: openshift-serverless-logic spec: channel: alpha # channel of an operator package to subscribe to - installPlanApproval: Automatic # Manual # whether the update should be installed automatically + installPlanApproval: Automatic # whether the update should be installed automatically name: logic-operator-rhel8 # name of the operator package source: redhat-operators # name of the catalog source sourceNamespace: openshift-marketplace diff --git a/docs/developer.md b/docs/developer.md index 5e57016fd..b6a535a79 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -90,6 +90,11 @@ To undeploy the controller from the cluster: make undeploy ``` +In a case if Profile contain plugin infrastructure manifests `/config/profile//plugin-infra` it can be deployed by: +```sh +make plugin-infra [PROFILE=] +``` + **UnDeploy the controller from the cluster:** ```sh diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index 0127eb403..f8f358cac 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -74,7 +74,7 @@ See [example](/examples/orchestrator.yaml) for a complete configuration of the o The orchestrator plugin has the following dependencies: - A Sonataflowplatform custom resource - created in the namespace of the Backstage CR. - Knativeeventing and Knativeserving custom resources to be created in the knative-eventing and knative-serving namespaces respectively. -- A set of NetworkPolicies to communicate to Knative resources created in the namespace of Backstage CR. +- A set of NetworkPolicies to allow traffic between Knative resources created in the namespace of Backstage CR, traffic for monitoring, and intra-namespace traffic. See [profile/rhdh/plugin-deps](/config/profile/rhdh/plugin-deps) The orchestrator-backend plugin uses the service **sonataflow-platform-data-index-service**, which is created by the SonataFlowPlatform CR. This service is used to communicate with the SonataFlow platform. @@ -95,8 +95,8 @@ The orchestrator-backend plugin uses the service **sonataflow-platform-data-inde ``` Current **default** implementation of the orchestrator plugin dependencies uses: -- the PstgreSQL database created bt Backstage for Orchestrator plugin, named **backstage_plugin_orchestrator** -- the Secret created by Backstage operator for the PstgreSQL with **POSTGRES_USER** and **POSTGRES_PASSWORD** keys as the database credentials in the Backstage CR namespace. +- the PostgreSQL database created by Backstage for Orchestrator plugin, named **backstage_plugin_orchestrator** +- the Secret created by Backstage operator for the PostgreSQL with **POSTGRES_USER** and **POSTGRES_PASSWORD** keys as the database credentials in the Backstage CR namespace. - the Service created by Backstage operator for the PostgreSQL database with the name **backstage-psql-{{backstage-name}}** in the Backstage CR namespace. **Note:** This default implementation is not recommended for production. For production environments, you should configure an external database connection instead (**TODO: Provide an example with an external database**). @@ -104,8 +104,12 @@ Current **default** implementation of the orchestrator plugin dependencies uses: **Known issue:** Since DB secret with credentials is automatically generated by the Operator every time new Backstage CR created and sonataflowplatform is not recreated and so uses the "old" secret, the orchestrator plugin will not work properly. To resolve this, the user must manually recreate the SonataFlowPlatform CR or its pods. -The SonataFlowPlatform CR requires the SonataFlow operator to be installed. In an OpenShift environment, it is done by installing the OpenShift Serverless Operator and the OpenShift Serverless Logic Operator. +The SonataFlowPlatform CR requires the SonataFlow operator to be installed. In an OpenShift environment, it is done by installing the OpenShift Serverless Operator and the OpenShift Serverless Logic Operator by installing respective OLM Subscriptions (see [infra-serverless.yaml](/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml) and [infra-sonataflow.yaml](/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml)). + +**Note:** Current Subscriptions configuration considers **Automatic** install plan (**spec.installPlanApproval: Automatic**), sonsider to change it to **Manual** if you want to control the installation of the SonataFlow operator. + Additionally, to enable the Backstage operator to work with the SonataFlow platform, its ServiceAccount must be granted the appropriate permissions. These manifests are defined as plugin infrastructure in the profile/rhdh/operator/plugin-infra/orchestrator directory. + **Note:** Using plugin infrastructure (plugin-infra) can be risky in production, as it modifies cluster-scoped resources. \ No newline at end of file diff --git a/examples/orchestrator.yaml b/examples/orchestrator.yaml index 835189b96..599d2d7e9 100644 --- a/examples/orchestrator.yaml +++ b/examples/orchestrator.yaml @@ -58,10 +58,6 @@ data: - POST - PUT - DELETE - csp: - script-src: ["'self'", "'unsafe-inline'", "'unsafe-eval'"] - script-src-elem: ["'self'", "'unsafe-inline'", "'unsafe-eval'"] - connect-src: ["'self'", 'http:', 'https:', 'data:'] auth: externalAccess: - type: legacy From 1dffcc3b0b438cb750c94e996d1840c624e50725 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Tue, 6 May 2025 14:19:15 +0300 Subject: [PATCH 25/35] fixe Signed-off-by: gazarenkov --- docs/dynamic-plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index f8f358cac..ab3a9a3df 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -106,7 +106,7 @@ Since DB secret with credentials is automatically generated by the Operator ever The SonataFlowPlatform CR requires the SonataFlow operator to be installed. In an OpenShift environment, it is done by installing the OpenShift Serverless Operator and the OpenShift Serverless Logic Operator by installing respective OLM Subscriptions (see [infra-serverless.yaml](/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml) and [infra-sonataflow.yaml](/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml)). -**Note:** Current Subscriptions configuration considers **Automatic** install plan (**spec.installPlanApproval: Automatic**), sonsider to change it to **Manual** if you want to control the installation of the SonataFlow operator. +**Note:** Current Subscriptions configuration considers **Automatic** install plan (**spec.installPlanApproval: Automatic**), consider to change it to **Manual** if you want to control the installation of the SonataFlow operator. Additionally, to enable the Backstage operator to work with the SonataFlow platform, its ServiceAccount must be granted the appropriate permissions. From beacbb5ffdefaf5e0feece7e8b9434b798023942 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Tue, 6 May 2025 16:11:26 +0300 Subject: [PATCH 26/35] fix doc Signed-off-by: gazarenkov --- docs/dynamic-plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index ab3a9a3df..d61ef0682 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -106,7 +106,7 @@ Since DB secret with credentials is automatically generated by the Operator ever The SonataFlowPlatform CR requires the SonataFlow operator to be installed. In an OpenShift environment, it is done by installing the OpenShift Serverless Operator and the OpenShift Serverless Logic Operator by installing respective OLM Subscriptions (see [infra-serverless.yaml](/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml) and [infra-sonataflow.yaml](/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml)). -**Note:** Current Subscriptions configuration considers **Automatic** install plan (**spec.installPlanApproval: Automatic**), consider to change it to **Manual** if you want to control the installation of the SonataFlow operator. +**Note:** Current Subscriptions configuration considers **Automatic** install plan (**spec.installPlanApproval: Automatic**), consider to change it to **Manual** if you want to control the installation of the openshift-serverless and openshift-serverless-logic operators (see [Operator Installation with OLM](https://olm.operatorframework.io/docs/tasks/install-operator-with-olm) for more details). Additionally, to enable the Backstage operator to work with the SonataFlow platform, its ServiceAccount must be granted the appropriate permissions. From d05219a75f4152d190bd0811a583bb8609c6fe79 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Tue, 6 May 2025 16:13:38 +0300 Subject: [PATCH 27/35] fix doc Signed-off-by: gazarenkov --- docs/dynamic-plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index d61ef0682..1098bfcb7 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -106,7 +106,7 @@ Since DB secret with credentials is automatically generated by the Operator ever The SonataFlowPlatform CR requires the SonataFlow operator to be installed. In an OpenShift environment, it is done by installing the OpenShift Serverless Operator and the OpenShift Serverless Logic Operator by installing respective OLM Subscriptions (see [infra-serverless.yaml](/config/profile/rhdh/plugin-infra/orchestrator/infra-serverless.yaml) and [infra-sonataflow.yaml](/config/profile/rhdh/plugin-infra/orchestrator/infra-sonataflow.yaml)). -**Note:** Current Subscriptions configuration considers **Automatic** install plan (**spec.installPlanApproval: Automatic**), consider to change it to **Manual** if you want to control the installation of the openshift-serverless and openshift-serverless-logic operators (see [Operator Installation with OLM](https://olm.operatorframework.io/docs/tasks/install-operator-with-olm) for more details). +**Note:** Current Subscriptions configuration uses **Automatic** install plan (**spec.installPlanApproval: Automatic**), consider to change it to **Manual** if you want to control the installation of the operators (see [Operator Installation with OLM](https://olm.operatorframework.io/docs/tasks/install-operator-with-olm) for more details). Additionally, to enable the Backstage operator to work with the SonataFlow platform, its ServiceAccount must be granted the appropriate permissions. From 0b2b12e83dd433275416c39adbe2ad626c149e88 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Fri, 9 May 2025 10:23:30 +0300 Subject: [PATCH 28/35] bundle Signed-off-by: gazarenkov --- ...kstage-operator.clusterserviceversion.yaml | 2 +- ...plugin-deps-orchestrator_v1_configmap.yaml | 130 ------------------ 2 files changed, 1 insertion(+), 131 deletions(-) delete mode 100644 bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index dbcaf35ba..27904663b 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.7 - createdAt: "2025-05-02T11:38:51Z" + createdAt: "2025-05-09T07:22:41Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed diff --git a/bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml deleted file mode 100644 index 65f2352fa..000000000 --- a/bundle/rhdh/manifests/rhdh-plugin-deps-orchestrator_v1_configmap.yaml +++ /dev/null @@ -1,130 +0,0 @@ -apiVersion: v1 -data: - sonataflow.yaml: | - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: allow-knative-to-sonataflow-and-workflows # hardcoded - spec: - podSelector: {} - ingress: - - from: - - namespaceSelector: - matchLabels: - # Allow knative events to be delivered to workflows. - kubernetes.io/metadata.name: knative-eventing - - namespaceSelector: - matchLabels: - # Allow auxiliary knative function for workflow (such as m2k-save-transformation) - kubernetes.io/metadata.name: knative-serving - --- - # NetworkPolicy to unblock incoming traffic to the namespace - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: allow-external-communication # hardcoded - spec: - podSelector: {} - ingress: - - from: - - namespaceSelector: - matchLabels: - # Allow knative events to be delivered to workflows. - kubernetes.io/metadata.name: openshift-ingress - --- - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: allow-intra-network # hardcoded - spec: - # Apply this policy to all pods in the namespace - podSelector: {} - # Specify policy type as 'Ingress' to control incoming traffic rules - policyTypes: - - Ingress - ingress: - - from: - # Allow ingress from any pod within the same namespace - - podSelector: {} - --- - # NetworkPolicy to allow openshift-user-workload-monitoring pods to access all pods within the workflow's namespace - apiVersion: networking.k8s.io/v1 - kind: NetworkPolicy - metadata: - name: allow-monitoring-to-sonataflow-and-workflows # hardcoded - spec: - # Apply this policy to all pods in the namespace - podSelector: {} - # Specify policy type as 'Ingress' to control incoming traffic rules - policyTypes: - - Ingress - ingress: - - from: - - namespaceSelector: - matchLabels: - # Allow openshift-user-workload-monitoring pods to access the workflow. - kubernetes.io/metadata.name: openshift-user-workload-monitoring - --- - apiVersion: operator.knative.dev/v1beta1 - kind: KnativeEventing - metadata: - name: knative-eventing - namespace: knative-eventing - spec: - Registry: {} - --- - apiVersion: operator.knative.dev/v1beta1 - kind: KnativeServing - metadata: - name: knative-serving - namespace: knative-serving - spec: - controller-custom-certs: - name: "" - type: "" - registry: {} - --- - apiVersion: sonataflow.org/v1alpha08 - kind: SonataFlowPlatform - metadata: - name: sonataflow-platform - spec: - monitoring: - enabled: true - build: - template: - resources: - requests: - memory: 64Mi - cpu: 250m - limits: - memory: 1Gi - cpu: 500m - services: - dataIndex: - enabled: true - persistence: - postgresql: - secretRef: - name: backstage-psql-secret-{{backstage-name}} - userKey: POSTGRES_USER - passwordKey: POSTGRES_PASSWORD - serviceRef: - name: backstage-psql-{{backstage-name}} - namespace: {{backstage-ns}} - databaseName: backstage_plugin_orchestrator - jobService: - enabled: true - persistence: - postgresql: - secretRef: - name: backstage-psql-secret-{{backstage-name}} - userKey: POSTGRES_USER - passwordKey: POSTGRES_PASSWORD - serviceRef: - name: backstage-psql-{{backstage-name}} - namespace: {{backstage-ns}} - databaseName: backstage_plugin_orchestrator -kind: ConfigMap -metadata: - name: rhdh-plugin-deps-orchestrator From 1f95234529f4987b9913ab2e4beba7a4c3e36f80 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Fri, 9 May 2025 16:02:50 +0300 Subject: [PATCH 29/35] check plugin-infra dir Signed-off-by: gazarenkov --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c66d4a6b1..f07c86606 100644 --- a/Makefile +++ b/Makefile @@ -366,7 +366,11 @@ undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/. .PHONY: plugin-infra plugin-infra: - $(KUSTOMIZE) build config/profile/$(PROFILE)/plugin-infra | $(KUBECTL) apply -f - + @if [ -d "config/profile/$(PROFILE)/plugin-infra" ]; then \ + $(KUSTOMIZE) build config/profile/$(PROFILE)/plugin-infra | $(KUBECTL) apply -f -; \ + else \ + echo "Directory config/profile/$(PROFILE)/plugin-infra does not exist."; \ + fi ##@ OLM Deployment # It has to be the same namespace as ./config/default/kustomization.yaml -> namespace From ea98c6a4b340c38b19f55cc41b1febe21628690c Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Mon, 12 May 2025 15:24:48 +0300 Subject: [PATCH 30/35] simplify config Signed-off-by: gazarenkov --- .../profile/rhdh/plugin-deps/sonataflow.yaml | 9 ------ examples/orchestrator.yaml | 28 ------------------- 2 files changed, 37 deletions(-) diff --git a/config/profile/rhdh/plugin-deps/sonataflow.yaml b/config/profile/rhdh/plugin-deps/sonataflow.yaml index d7fd07c3b..914dbe58f 100644 --- a/config/profile/rhdh/plugin-deps/sonataflow.yaml +++ b/config/profile/rhdh/plugin-deps/sonataflow.yaml @@ -88,15 +88,6 @@ metadata: spec: monitoring: enabled: true - build: - template: - resources: - requests: - memory: 64Mi - cpu: 250m - limits: - memory: 1Gi - cpu: 500m services: dataIndex: enabled: true diff --git a/examples/orchestrator.yaml b/examples/orchestrator.yaml index 599d2d7e9..e354dcf70 100644 --- a/examples/orchestrator.yaml +++ b/examples/orchestrator.yaml @@ -48,22 +48,6 @@ metadata: name: app-config-rhdh data: app-config-rhdh.yaml: |- - app: - title: Red Hat Developer Hub - backend: - cors: - credentials: true - methods: - - GET - - POST - - PUT - - DELETE - auth: - externalAccess: - - type: legacy - options: - subject: legacy-default-config - secret: "${BACKEND_SECRET}" auth: environment: development providers: @@ -71,15 +55,6 @@ data: # using the guest user to query the '/api/dynamic-plugins-info/loaded-plugins' endpoint. dangerouslyAllowOutsideDevelopment: true --- -apiVersion: v1 -kind: Secret -metadata: - name: rhdh-secrets -type: Opaque -stringData: - #ROUTE_URL: "https://backstage-bs1-backstage.apps.rosa.pvmvb-xo4tu-ri7.1py9.p3.openshiftapps.com" - BACKEND_SECRET: "R2FxRVNrcmwzYzhhN3l0V1VRcnQ3L1pLT09WaVhDNUEK" ---- apiVersion: rhdh.redhat.com/v1alpha3 kind: Backstage metadata: @@ -89,7 +64,4 @@ spec: appConfig: configMaps: - name: app-config-rhdh - extraEnvs: - secrets: - - name: rhdh-secrets dynamicPluginsConfigMapName: orchestrator-plugin From 2d9589084db0ce73deb431d76fd15706baea209e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 12 May 2025 12:25:49 +0000 Subject: [PATCH 31/35] Regenerate bundle/installer manifests Co-authored-by: gazarenkov --- .../backstage-operator.clusterserviceversion.yaml | 2 +- .../backstage-operator.clusterserviceversion.yaml | 2 +- bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml | 9 --------- dist/rhdh/install.yaml | 9 --------- 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index c00b935bf..1d54e12ca 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -35,7 +35,7 @@ metadata: } } ] - createdAt: "2025-05-02T11:38:50Z" + createdAt: "2025-05-12T12:25:43Z" description: Backstage Operator operators.operatorframework.io/builder: operator-sdk-v1.37.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index 27904663b..ef9093719 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.7 - createdAt: "2025-05-09T07:22:41Z" + createdAt: "2025-05-12T12:25:44Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed diff --git a/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml index e1eeaf0f8..447ca1301 100644 --- a/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml @@ -91,15 +91,6 @@ data: spec: monitoring: enabled: true - build: - template: - resources: - requests: - memory: 64Mi - cpu: 250m - limits: - memory: 1Gi - cpu: 500m services: dataIndex: enabled: true diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index 68ef1837b..c1d50bdb8 100644 --- a/dist/rhdh/install.yaml +++ b/dist/rhdh/install.yaml @@ -2069,15 +2069,6 @@ data: spec: monitoring: enabled: true - build: - template: - resources: - requests: - memory: 64Mi - cpu: 250m - limits: - memory: 1Gi - cpu: 500m services: dataIndex: enabled: true From 9a71375d30e95ea313732fc77359297c7f73eca2 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Mon, 12 May 2025 15:53:28 +0300 Subject: [PATCH 32/35] fix Signed-off-by: gazarenkov --- config/profile/rhdh/plugin-deps/sonataflow.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/profile/rhdh/plugin-deps/sonataflow.yaml b/config/profile/rhdh/plugin-deps/sonataflow.yaml index 914dbe58f..b020fcd6f 100644 --- a/config/profile/rhdh/plugin-deps/sonataflow.yaml +++ b/config/profile/rhdh/plugin-deps/sonataflow.yaml @@ -27,7 +27,7 @@ spec: - namespaceSelector: matchLabels: # Allow knative events to be delivered to workflows. - kubernetes.io/metadata.name: openshift-ingress + policy-group.network.openshift.io/ingress: "" --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy From ba8806a7bd28640103478ce59fbf695f74ac9dad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 12 May 2025 12:54:45 +0000 Subject: [PATCH 33/35] Regenerate bundle/installer manifests Co-authored-by: gazarenkov --- .../manifests/backstage-operator.clusterserviceversion.yaml | 2 +- .../manifests/backstage-operator.clusterserviceversion.yaml | 2 +- bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml | 2 +- dist/rhdh/install.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index 1d54e12ca..b822ba80d 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -35,7 +35,7 @@ metadata: } } ] - createdAt: "2025-05-12T12:25:43Z" + createdAt: "2025-05-12T12:54:39Z" description: Backstage Operator operators.operatorframework.io/builder: operator-sdk-v1.37.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index ef9093719..cde2ec2d9 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.7 - createdAt: "2025-05-12T12:25:44Z" + createdAt: "2025-05-12T12:54:40Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed diff --git a/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml index 447ca1301..5ec78accf 100644 --- a/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml @@ -30,7 +30,7 @@ data: - namespaceSelector: matchLabels: # Allow knative events to be delivered to workflows. - kubernetes.io/metadata.name: openshift-ingress + policy-group.network.openshift.io/ingress: "" --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index c1d50bdb8..b58b24499 100644 --- a/dist/rhdh/install.yaml +++ b/dist/rhdh/install.yaml @@ -2008,7 +2008,7 @@ data: - namespaceSelector: matchLabels: # Allow knative events to be delivered to workflows. - kubernetes.io/metadata.name: openshift-ingress + policy-group.network.openshift.io/ingress: "" --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy From 757dba1d9f726a788598affe44e4ce17f5801983 Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Mon, 12 May 2025 15:57:45 +0300 Subject: [PATCH 34/35] fix Signed-off-by: gazarenkov --- bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml index 447ca1301..5ec78accf 100644 --- a/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-plugin-deps_v1_configmap.yaml @@ -30,7 +30,7 @@ data: - namespaceSelector: matchLabels: # Allow knative events to be delivered to workflows. - kubernetes.io/metadata.name: openshift-ingress + policy-group.network.openshift.io/ingress: "" --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy From 81e4534e8b498ced98737db01b464e63829e040e Mon Sep 17 00:00:00 2001 From: gazarenkov Date: Wed, 14 May 2025 10:35:40 +0300 Subject: [PATCH 35/35] plugin-infra-undeploy Signed-off-by: gazarenkov --- Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Makefile b/Makefile index f07c86606..8860e115b 100644 --- a/Makefile +++ b/Makefile @@ -371,6 +371,15 @@ plugin-infra: else \ echo "Directory config/profile/$(PROFILE)/plugin-infra does not exist."; \ fi + +.PHONY: plugin-infra-undeploy +plugin-infra-undeploy: + @if [ -d "config/profile/$(PROFILE)/plugin-infra" ]; then \ + $(KUSTOMIZE) build config/profile/$(PROFILE)/plugin-infra | $(KUBECTL) delete -f -; \ + else \ + echo "Directory config/profile/$(PROFILE)/plugin-infra does not exist."; \ + fi + ##@ OLM Deployment # It has to be the same namespace as ./config/default/kustomization.yaml -> namespace