This is an automated email from the ASF dual-hosted git repository.

treblereel pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git


The following commit(s) were added to refs/heads/main by this push:
     new ae23499ca3a kie-tools#2630: Extend kn-workflow CLI to Generate K8s 
secrets for workflows (#2763)
ae23499ca3a is described below

commit ae23499ca3a273c8c5f50e75c4268d0f613317fe
Author: Dmitrii Tikhomirov <[email protected]>
AuthorDate: Tue Feb 4 11:37:42 2025 -0800

    kie-tools#2630: Extend kn-workflow CLI to Generate K8s secrets for 
workflows (#2763)
---
 .../pkg/command/deploy_undeploy_common.go          |  66 +++++++---
 .../kn-plugin-workflow/pkg/metadata/constants.go   |   2 +-
 packages/sonataflow-operator/workflowproj/go.mod   |   1 +
 packages/sonataflow-operator/workflowproj/go.sum   |   1 +
 .../sonataflow-operator/workflowproj/operator.go   |  23 +++-
 .../testdata/workflows/secret.properties           |  25 ++++
 .../workflowproj/workflowproj.go                   | 141 +++++++++++++++++++--
 .../workflowproj/workflowproj_test.go              | 107 ++++++++++++++++
 8 files changed, 334 insertions(+), 32 deletions(-)

diff --git a/packages/kn-plugin-workflow/pkg/command/deploy_undeploy_common.go 
b/packages/kn-plugin-workflow/pkg/command/deploy_undeploy_common.go
index 6d8bd8d2a5b..bd9c3d7305c 100644
--- a/packages/kn-plugin-workflow/pkg/command/deploy_undeploy_common.go
+++ b/packages/kn-plugin-workflow/pkg/command/deploy_undeploy_common.go
@@ -32,26 +32,27 @@ import (
 )
 
 type DeployUndeployCmdConfig struct {
-       EmptyNameSpace             bool
-       NameSpace                  string
-       KubectlContext             string
-       SonataFlowFile             string
-       CustomGeneratedManifestDir string
-       TempDir                    string
-       ApplicationPropertiesPath  string
-       SubflowsDir                string
-       SpecsDir                   string
-       SchemasDir                 string
-       CustomManifestsFileDir     string
-       DefaultDashboardsFolder    string
-       Profile                    string
-       Image                      string
-       SchemasFilesPath           []string
-       SpecsFilesPath             map[string]string
-       SubFlowsFilesPath          []string
-       DashboardsPath             []string
-       Minify                     bool
-       Wait                                       bool
+       EmptyNameSpace                   bool
+       NameSpace                        string
+       KubectlContext                   string
+       SonataFlowFile                   string
+       CustomGeneratedManifestDir       string
+       TempDir                          string
+       ApplicationPropertiesPath        string
+       ApplicationSecretPropertiesPath  string
+       SubflowsDir                      string
+       SpecsDir                         string
+       SchemasDir                       string
+       CustomManifestsFileDir           string
+       DefaultDashboardsFolder          string
+       Profile                          string
+       Image                            string
+       SchemasFilesPath                 []string
+       SpecsFilesPath                   map[string]string
+       SubFlowsFilesPath                []string
+       DashboardsPath                   []string
+       Minify                           bool
+       Wait                                                     bool
 }
 
 func checkEnvironment(cfg *DeployUndeployCmdConfig) error {
@@ -119,6 +120,12 @@ func generateManifests(cfg *DeployUndeployCmdConfig) error 
{
                fmt.Printf(" - ✅ Properties file found: %s\n", 
cfg.ApplicationPropertiesPath)
        }
 
+       applicationSecretPropertiesPath := 
findApplicationSecretPropertiesPath(dir)
+       if applicationSecretPropertiesPath != "" {
+               cfg.ApplicationSecretPropertiesPath = 
applicationSecretPropertiesPath
+               fmt.Printf(" - ✅ Secret Properties file found: %s\n", 
cfg.ApplicationSecretPropertiesPath)
+       }
+
        supportFileExtensions := []string{metadata.JSONExtension, 
metadata.YAMLExtension, metadata.YMLExtension}
 
        fmt.Println("🔍 Looking for specs files...")
@@ -182,6 +189,14 @@ func generateManifests(cfg *DeployUndeployCmdConfig) error 
{
                handler.WithAppProperties(appIO)
        }
 
+       if cfg.ApplicationSecretPropertiesPath != "" {
+               appIO, err := 
common.MustGetFile(cfg.ApplicationSecretPropertiesPath)
+               if err != nil {
+                       return err
+               }
+               handler.WithSecretProperties(appIO)
+       }
+
        for _, subflow := range cfg.SubFlowsFilesPath {
                specIO, err := common.MustGetFile(subflow)
                if err != nil {
@@ -246,6 +261,17 @@ func findApplicationPropertiesPath(directoryPath string) 
string {
        return filePath
 }
 
+func findApplicationSecretPropertiesPath(directoryPath string) string {
+       filePath := filepath.Join(directoryPath, 
metadata.ApplicationSecretProperties)
+
+       fileInfo, err := os.Stat(filePath)
+       if err != nil || fileInfo.IsDir() {
+               return ""
+       }
+
+       return filePath
+}
+
 func setupConfigManifestPath(cfg *DeployUndeployCmdConfig) error {
 
        if len(cfg.CustomGeneratedManifestDir) == 0 {
diff --git a/packages/kn-plugin-workflow/pkg/metadata/constants.go 
b/packages/kn-plugin-workflow/pkg/metadata/constants.go
index 9cd7a348bd7..37d9284e796 100644
--- a/packages/kn-plugin-workflow/pkg/metadata/constants.go
+++ b/packages/kn-plugin-workflow/pkg/metadata/constants.go
@@ -83,7 +83,7 @@ const (
        YMLSWExtension        = "sw.yml"
        JSONSWExtension       = "sw.json"
        ApplicationProperties = "application.properties"
-
+       ApplicationSecretProperties = "secret.properties"
        ManifestServiceFilesKind = "SonataFlow"
 
        DockerInternalPort = "8080/tcp"
diff --git a/packages/sonataflow-operator/workflowproj/go.mod 
b/packages/sonataflow-operator/workflowproj/go.mod
index ab3c3d06d98..4470e692ece 100644
--- a/packages/sonataflow-operator/workflowproj/go.mod
+++ b/packages/sonataflow-operator/workflowproj/go.mod
@@ -7,6 +7,7 @@ replace 
github.com/apache/incubator-kie-tools/packages/sonataflow-operator/api =
 
 require (
        github.com/apache/incubator-kie-tools/packages/sonataflow-operator/api 
v0.0.0
+       github.com/magiconair/properties v1.8.7
        github.com/pb33f/libopenapi v0.8.4
        github.com/pkg/errors v0.9.1
        github.com/santhosh-tekuri/jsonschema/v5 v5.3.0
diff --git a/packages/sonataflow-operator/workflowproj/go.sum 
b/packages/sonataflow-operator/workflowproj/go.sum
index 0ecd382c898..4c5684b5f03 100644
--- a/packages/sonataflow-operator/workflowproj/go.sum
+++ b/packages/sonataflow-operator/workflowproj/go.sum
@@ -86,6 +86,7 @@ github.com/kr/text v0.1.0/go.mod 
h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod 
h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/leodido/go-urn v1.4.0 
h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/magiconair/properties v1.8.7 
h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
 github.com/mailru/easyjson v0.7.7 
h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 github.com/mailru/easyjson v0.7.7/go.mod 
h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod 
h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
diff --git a/packages/sonataflow-operator/workflowproj/operator.go 
b/packages/sonataflow-operator/workflowproj/operator.go
index f2290492570..458ec4c2ea1 100644
--- a/packages/sonataflow-operator/workflowproj/operator.go
+++ b/packages/sonataflow-operator/workflowproj/operator.go
@@ -31,9 +31,12 @@ import (
 )
 
 const (
-       workflowUserConfigMapNameSuffix = "-props"
+       workflowUserConfigMapNameSuffix       = "-props"
+       workflowUserSecretConfigMapNameSuffix = "-secrets"
        // ApplicationPropertiesFileName is the default application properties 
file name holding user properties
-       ApplicationPropertiesFileName      = "application.properties"
+       ApplicationPropertiesFileName = "application.properties"
+       // SecretPropertiesFileName is the default application secret 
properties file name holding user secret properties
+       SecretPropertiesFileName           = "secret.properties"
        workflowManagedConfigMapNameSuffix = "-managed-props"
        // LabelApp key to use among object selectors, "app" is used among k8s 
applications to group objects in some UI consoles
        LabelApp = "app"
@@ -90,6 +93,11 @@ func GetManagedPropertiesFileName(workflow 
*operatorapi.SonataFlow) string {
        return fmt.Sprintf("application-%s.properties", profile)
 }
 
+// GetWorkflowUserSecretPropertiesConfigMapName gets the default ConfigMap 
name that holds the user application secrets property for the given workflow
+func GetWorkflowUserSecretPropertiesConfigMapName(workflow 
*operatorapi.SonataFlow) string {
+       return workflow.Name + workflowUserSecretConfigMapNameSuffix
+}
+
 // GetDefaultLabels gets the default labels based on the given workflow.
 func GetDefaultLabels(workflow *operatorapi.SonataFlow) map[string]string {
        labels := map[string]string{
@@ -143,6 +151,17 @@ func CreateNewUserPropsConfigMap(workflow 
*operatorapi.SonataFlow) *corev1.Confi
        }
 }
 
+func CreateNewSecretPropsConfigMap(workflow *operatorapi.SonataFlow) 
*corev1.Secret {
+       return &corev1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      
GetWorkflowUserSecretPropertiesConfigMapName(workflow),
+                       Namespace: workflow.Namespace,
+                       Labels:    GetMergedLabels(workflow),
+               },
+       }
+
+}
+
 // CreateNewManagedPropsConfigMap creates a new ConfigMap object to hold the 
managed application properties of the workflows.
 func CreateNewManagedPropsConfigMap(workflow *operatorapi.SonataFlow, 
properties string) *corev1.ConfigMap {
        return &corev1.ConfigMap{
diff --git 
a/packages/sonataflow-operator/workflowproj/testdata/workflows/secret.properties
 
b/packages/sonataflow-operator/workflowproj/testdata/workflows/secret.properties
new file mode 100644
index 00000000000..5b302b06e5b
--- /dev/null
+++ 
b/packages/sonataflow-operator/workflowproj/testdata/workflows/secret.properties
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+MY_USER_PASSWORD=Super$ecretP@ssw0rd!
+DATABASE_URL=https://secure.example.com
+API_KEY=12345-ABCDE-67890-FGHIJ
+ANOTHER_KEY=ThisIs@Complex#Value123
+DB_ROOT_PASSWORD=!P@$$w0rd_123#
+JWT_SECRET=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
+ENCRYPTION_KEY=5up3r-53cr37-K3y!
+KEY_WITH_NUMBERS_123=ValueWith123Numbers
+SIMPLE_KEY=Simple_Value_2024
diff --git a/packages/sonataflow-operator/workflowproj/workflowproj.go 
b/packages/sonataflow-operator/workflowproj/workflowproj.go
index 09b46b9ad71..7931516d7a9 100644
--- a/packages/sonataflow-operator/workflowproj/workflowproj.go
+++ b/packages/sonataflow-operator/workflowproj/workflowproj.go
@@ -23,9 +23,12 @@ import (
        "context"
        "fmt"
        "io"
+       "regexp"
        "sort"
        "strings"
 
+       "github.com/magiconair/properties"
+
        "github.com/pkg/errors"
        "github.com/serverlessworkflow/sdk-go/v2/model"
        "github.com/serverlessworkflow/sdk-go/v2/parser"
@@ -56,6 +59,8 @@ type WorkflowProjectHandler interface {
        WithWorkflow(reader io.Reader) WorkflowProjectHandler
        // WithAppProperties reader for a file or the content stream of a 
workflow application properties.
        WithAppProperties(reader io.Reader) WorkflowProjectHandler
+       // WithSecretProperties reader for a file or the content stream of a 
workflow application properties.
+       WithSecretProperties(reader io.Reader) WorkflowProjectHandler
        // AddResource reader for a file or the content stream of any resource 
needed by the workflow. E.g. an OpenAPI specification file.
        // Name is required, should match the workflow function definition.
        AddResource(name string, reader io.Reader) WorkflowProjectHandler
@@ -75,6 +80,8 @@ type WorkflowProject struct {
        Workflow *operatorapi.SonataFlow
        // Properties the application properties for the workflow
        Properties *corev1.ConfigMap
+       //SecretProperties the secret properties for the workflow
+       SecretProperties *corev1.Secret
        // Resources any resource that this workflow requires, like an OpenAPI 
specification file.
        Resources []*corev1.ConfigMap
 }
@@ -98,15 +105,16 @@ func New(namespace string) WorkflowProjectHandler {
 }
 
 type workflowProjectHandler struct {
-       name             string
-       namespace        string
-       profile          metadata.ProfileType
-       scheme           *runtime.Scheme
-       project          WorkflowProject
-       rawWorkflow      io.Reader
-       rawAppProperties io.Reader
-       rawResources     map[string][]*resource
-       parsed           bool
+       name                string
+       namespace           string
+       profile             metadata.ProfileType
+       scheme              *runtime.Scheme
+       project             WorkflowProject
+       rawWorkflow         io.Reader
+       rawAppProperties    io.Reader
+       rawSecretProperties io.Reader
+       rawResources        map[string][]*resource
+       parsed              bool
 }
 
 func (w *workflowProjectHandler) Named(name string) WorkflowProjectHandler {
@@ -133,6 +141,12 @@ func (w *workflowProjectHandler) WithAppProperties(reader 
io.Reader) WorkflowPro
        return w
 }
 
+func (w *workflowProjectHandler) WithSecretProperties(reader io.Reader) 
WorkflowProjectHandler {
+       w.rawSecretProperties = reader
+       w.parsed = false
+       return w
+}
+
 func (w *workflowProjectHandler) AddResource(name string, reader io.Reader) 
WorkflowProjectHandler {
        return w.AddResourceAt(name, defaultResourcePath, reader)
 }
@@ -163,6 +177,12 @@ func (w *workflowProjectHandler) 
SaveAsKubernetesManifests(path string) error {
                        return err
                }
        }
+       if w.project.SecretProperties != nil {
+               fileCount++
+               if err := saveAsKubernetesManifest(w.project.SecretProperties, 
path, fileCount); err != nil {
+                       return err
+               }
+       }
        for _, r := range w.project.Resources {
                fileCount++
                if err := saveAsKubernetesManifest(r, path, fileCount); err != 
nil {
@@ -196,6 +216,9 @@ func (w *workflowProjectHandler) parseRawProject() error {
        if err := w.parseRawAppProperties(); err != nil {
                return err
        }
+       if err := w.parseRawSecretProperties(); err != nil {
+               return err
+       }
        if err := w.parseRawResources(); err != nil {
                return err
        }
@@ -261,6 +284,52 @@ func (w *workflowProjectHandler) parseRawAppProperties() 
error {
        return nil
 }
 
+func (w *workflowProjectHandler) parseRawSecretProperties() error {
+       if w.rawSecretProperties == nil {
+               return nil
+       }
+       secretPropsContent, err := io.ReadAll(w.rawSecretProperties)
+       if err != nil {
+               return fmt.Errorf("Failed to read secret properties: %w", err)
+       }
+       secrets, err := properties.Load(secretPropsContent, properties.UTF8)
+       if err != nil {
+               return fmt.Errorf("Failed to load secret properties: %w", err)
+       }
+       if len(secrets.Map()) == 0 {
+               return nil //Do not create a secret if there are no secrets
+       }
+
+       w.project.SecretProperties = 
CreateNewSecretPropsConfigMap(w.project.Workflow)
+       w.project.SecretProperties.StringData = map[string]string{}
+       for key, value := range secrets.Map() {
+               normalizedEnvNames, err := normalizeEnvNames(key)
+               if err != nil {
+                       return err
+               }
+               for _, normalizedEnvName := range normalizedEnvNames {
+                       w.project.SecretProperties.StringData[key] = value
+                       env := corev1.EnvVar{
+                               Name: normalizedEnvName,
+                               ValueFrom: &corev1.EnvVarSource{
+                                       SecretKeyRef: &corev1.SecretKeySelector{
+                                               LocalObjectReference: 
corev1.LocalObjectReference{
+                                                       Name: 
w.project.SecretProperties.Name,
+                                               },
+                                               Key: key,
+                                       },
+                               },
+                       }
+                       w.project.Workflow.Spec.PodTemplate.Container.Env = 
append(w.project.Workflow.Spec.PodTemplate.Container.Env, env)
+               }
+       }
+
+       if err = SetTypeToObject(w.project.SecretProperties, w.scheme); err != 
nil {
+               return err
+       }
+       return nil
+}
+
 func (w *workflowProjectHandler) parseRawResources() error {
        if len(w.rawResources) == 0 {
                return nil
@@ -338,3 +407,57 @@ func isProfile(workflow *operatorapi.SonataFlow, 
profileType metadata.ProfileTyp
        }
        return metadata.ProfileType(profile) == profileType
 }
+
+var envProfilesExpr = 
regexp.MustCompile(`^%([A-Za-z0-9_-]+(?:,[A-Za-z0-9_-]+)*)\.(.+)$`)
+
+func normalizeEnvNames(names string) ([]string, error) {
+       matches := envProfilesExpr.FindStringSubmatch(names)
+       if matches == nil {
+               if normalized, err := normalizeEnvName(names); err != nil {
+                       return nil, err
+               } else {
+                       return []string{normalized}, nil
+               }
+       } else {
+               profiles := strings.Split(matches[1], ",")
+               propertyKey := matches[2]
+               var result []string
+               for _, profile := range profiles {
+                       normalized, err := normalizeEnvName("%" + profile + "_" 
+ propertyKey)
+                       if err != nil {
+                               return nil, err
+                       }
+                       result = append(result, normalized)
+               }
+               return result, nil
+       }
+}
+
+func normalizeEnvName(original string) (string, error) {
+       name := original
+       name = strings.TrimSpace(name)
+
+       replacer := strings.NewReplacer(
+               " ", "_",
+               "-", "_",
+               ".", "_",
+               "\"", "_",
+               "'", "_",
+               "[", "_",
+               "]", "_",
+               "%", "_",
+       )
+       name = replacer.Replace(name)
+       name = strings.ToUpper(name)
+
+       if strings.HasSuffix(name, "_") {
+               name = name[:len(name)-1]
+       }
+
+       validName := regexp.MustCompile(`^[A-Z0-9_]+$`)
+       if !validName.MatchString(name) {
+               return "", fmt.Errorf("invalid environment variable name: %s 
(must only contain A-Z, 0-9, and _)", original)
+       }
+
+       return name, nil
+}
diff --git a/packages/sonataflow-operator/workflowproj/workflowproj_test.go 
b/packages/sonataflow-operator/workflowproj/workflowproj_test.go
index e028fba731f..d1f32559c65 100644
--- a/packages/sonataflow-operator/workflowproj/workflowproj_test.go
+++ b/packages/sonataflow-operator/workflowproj/workflowproj_test.go
@@ -28,6 +28,8 @@ import (
        "strings"
        "testing"
 
+       "github.com/magiconair/properties"
+
        "k8s.io/apimachinery/pkg/runtime/schema"
 
        "github.com/stretchr/testify/assert"
@@ -134,6 +136,48 @@ func 
Test_Handler_WorkflowMinimalAndPropsAndSpecAndGeneric(t *testing.T) {
        assert.NotEmpty(t, data)
 }
 
+func Test_Handler_WorkflowMinimalAndSecrets(t *testing.T) {
+       type env struct {
+               key              string
+               secretKeyRefName string
+       }
+       proj, err := New("default").
+               Named("minimal").
+               Profile(metadata.PreviewProfile).
+               WithWorkflow(getWorkflowMinimal()).
+               WithSecretProperties(getWorkflowSecretProperties()).
+               AsObjects()
+       assert.NoError(t, err)
+       assert.NotNil(t, proj.Workflow)
+       assert.NotNil(t, proj.SecretProperties)
+       assert.Equal(t, "minimal", proj.Workflow.Name)
+       assert.Equal(t, "minimal-secrets", proj.SecretProperties.Name)
+       assert.NotEmpty(t, proj.SecretProperties.StringData)
+
+       secretPropsContent, err := io.ReadAll(getWorkflowSecretProperties())
+       assert.NoError(t, err)
+       secrets, err := properties.Load(secretPropsContent, properties.UTF8)
+       assert.NoError(t, err)
+       envs := map[string]env{}
+
+       for _, value := range proj.Workflow.Spec.PodTemplate.Container.Env {
+               envs[value.Name] = env{key: value.ValueFrom.SecretKeyRef.Key, 
secretKeyRefName: value.ValueFrom.SecretKeyRef.Name}
+       }
+
+       for k, v := range secrets.Map() {
+               assert.Equal(t, v, proj.SecretProperties.StringData[k])
+               normalized, err := normalizeEnvNames(k)
+               for _, value := range normalized {
+                       assert.NoError(t, err)
+                       env, exists := envs[value]
+                       assert.True(t, exists)
+                       assert.Equal(t, k, env.key)
+                       assert.Equal(t, proj.SecretProperties.Name, 
env.secretKeyRefName)
+               }
+
+       }
+}
+
 func getResourceDataWithFileName(cms []*corev1.ConfigMap, fileName string) 
(string, error) {
        for i := range cms {
                if data, ok := cms[i].Data[fileName]; ok {
@@ -242,6 +286,65 @@ func TestWorkflowProjectHandler_Image(t *testing.T) {
        assert.Equal(t, "host/namespace/service:latest", 
proj.Workflow.Spec.PodTemplate.Container.Image)
 }
 
+func TestNormalizeEnvName(t *testing.T) {
+       type testCase struct {
+               input    string
+               expected []string
+               error    bool
+       }
+       tests := []testCase{
+               {"my-env", []string{"MY_ENV"}, false},
+               {"my.env.1", []string{"MY_ENV_1"}, false},
+               {"my.env-1", []string{"MY_ENV_1"}, false},
+               {"my-env.1", []string{"MY_ENV_1"}, false},
+               {"my-env-1$", []string{""}, true},
+               {"my-env-1&&", []string{""}, true},
+               {"", []string{""}, true},
+               {"$%&*", []string{""}, true},
+               {"a", []string{"A"}, false},
+               {"1", []string{"1"}, false},
+               {"_", []string{""}, true},
+               {"my env", []string{"MY_ENV"}, false},
+               {"  my env  ", []string{"MY_ENV"}, false},
+               {"-", []string{""}, true},
+               {".", []string{""}, true},
+               {"my-env-1234567890-long-name-with-dashes", 
[]string{"MY_ENV_1234567890_LONG_NAME_WITH_DASHES"}, false},
+               {"long-name-with-invalid-characters-@#$%^", []string{""}, true},
+               {"my-env-1@name", []string{""}, true},
+               {"A", []string{"A"}, false},
+               {"a1_b2", []string{"A1_B2"}, false},
+               {"a!!@#$b", []string{""}, true},
+               {"foo.\"bar\".baz", []string{"FOO__BAR__BAZ"}, false},
+               {"quarkus.'my-property'.foo", 
[]string{"QUARKUS__MY_PROPERTY__FOO"}, false},
+               {"myProperty[10]", []string{"MYPROPERTY_10"}, false},
+               {"my.config[0].value", []string{"MY_CONFIG_0__VALUE"}, false},
+               {"quarkus.\"myProperty\"[0].value", 
[]string{"QUARKUS__MYPROPERTY__0__VALUE"}, false},
+               {"quarkus.\"my-property\"[1].sub-name", 
[]string{"QUARKUS__MY_PROPERTY__1__SUB_NAME"}, false},
+               {"quarkus.myProperty..sub.value", 
[]string{"QUARKUS_MYPROPERTY__SUB_VALUE"}, false},
+               {"quarkus.[strange].key", []string{"QUARKUS__STRANGE__KEY"}, 
false},
+               {"quarkus.datasource.\"datasource-name\".jdbc.url", 
[]string{"QUARKUS_DATASOURCE__DATASOURCE_NAME__JDBC_URL"}, false},
+               {"%dev.quarkus.http.port", []string{"_DEV_QUARKUS_HTTP_PORT"}, 
false},
+               {"%staging.quarkus.http.test-port", 
[]string{"_STAGING_QUARKUS_HTTP_TEST_PORT"}, false},
+               {"%prod,dev.my.prop", []string{"_PROD_MY_PROP", 
"_DEV_MY_PROP"}, false},
+               {"%prod,dev.quarkus.datasource.\"datasource-name\".jdbc.url", 
[]string{"_PROD_QUARKUS_DATASOURCE__DATASOURCE_NAME__JDBC_URL",
+                       "_DEV_QUARKUS_DATASOURCE__DATASOURCE_NAME__JDBC_URL"}, 
false},
+       }
+
+       for _, test := range tests {
+               t.Run(test.input, func(t *testing.T) {
+                       actual, err := normalizeEnvNames(test.input)
+                       if test.error {
+                               assert.Error(t, err)
+                       } else {
+                               assert.NoError(t, err)
+                               for index, expected := range test.expected {
+                                       assert.Equal(t, expected, actual[index])
+                               }
+                       }
+               })
+       }
+}
+
 func getWorkflowMinimalInvalid() io.Reader {
        return 
mustGetFile("testdata/workflows/workflow-minimal-invalid.sw.json")
 }
@@ -266,6 +369,10 @@ func getSpecGeneric() io.Reader {
        return 
mustGetFile("testdata/workflows/specs/workflow-service-schema.json")
 }
 
+func getWorkflowSecretProperties() io.Reader {
+       return mustGetFile("testdata/workflows/secret.properties")
+}
+
 func mustGetFile(filepath string) io.Reader {
        file, err := os.OpenFile(filepath, os.O_RDONLY, os.ModePerm)
        if err != nil {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to