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]