This is an automated email from the ASF dual-hosted git repository.
ricardozanini pushed a commit to branch main
in repository
https://gitbox.apache.org/repos/asf/incubator-kie-kogito-serverless-operator.git
The following commit(s) were added to refs/heads/main by this push:
new a7a158f3 [KOGITO-9890] Generate safe volume naming based on DNS 1035
(#272)
a7a158f3 is described below
commit a7a158f36536250d65bab68d741085f8ebfe56ae
Author: Ricardo Zanini <[email protected]>
AuthorDate: Fri Oct 13 11:15:30 2023 -0300
[KOGITO-9890] Generate safe volume naming based on DNS 1035 (#272)
* [KOGITO-9890] Generate safe volume naming based on DNS 1035
Signed-off-by: Ricardo Zanini <[email protected]>
* Remove flacky test, and unused function
Signed-off-by: Ricardo Zanini <[email protected]>
---------
Signed-off-by: Ricardo Zanini <[email protected]>
---
controllers/profiles/dev/object_creators_dev.go | 3 +-
controllers/profiles/dev/profile_dev_test.go | 33 ++++++++++--
controllers/profiles/dev/states_dev.go | 3 +-
...onataflow.org_v1alpha08_sonataflow-metainf.yaml | 62 ++++++++++++++++++++++
.../v1_configmap_greetings_staticfiles.yaml | 24 +++++++++
test/yaml.go | 21 +++++---
utils/kubernetes/naming.go | 49 +++++++++++++++++
utils/strings.go | 7 ---
8 files changed, 179 insertions(+), 23 deletions(-)
diff --git a/controllers/profiles/dev/object_creators_dev.go
b/controllers/profiles/dev/object_creators_dev.go
index 9b760b7d..609621c7 100644
--- a/controllers/profiles/dev/object_creators_dev.go
+++ b/controllers/profiles/dev/object_creators_dev.go
@@ -26,7 +26,6 @@ import (
"github.com/kiegroup/kogito-serverless-operator/controllers/profiles"
"github.com/kiegroup/kogito-serverless-operator/controllers/profiles/common"
"github.com/kiegroup/kogito-serverless-operator/controllers/workflowdef"
- "github.com/kiegroup/kogito-serverless-operator/utils"
kubeutil
"github.com/kiegroup/kogito-serverless-operator/utils/kubernetes"
"github.com/kiegroup/kogito-serverless-operator/workflowproj"
)
@@ -139,7 +138,7 @@ func mountDevConfigMapsMutateVisitor(flowDefCM, propsCM
*corev1.ConfigMap, workf
}
// the resource configMap needs a specific dir,
inside the src/main/resources
// to avoid clashing with other configMaps
trying to mount on the same dir, we create one projected per path
- volumeMountName :=
configMapExternalResourcesVolumeNamePrefix +
utils.PathToString(workflowResCM.WorkflowPath)
+ volumeMountName :=
kubeutil.MustSafeDNS1035(configMapExternalResourcesVolumeNamePrefix,
workflowResCM.WorkflowPath)
volumeMounts =
kubeutil.VolumeMountAdd(volumeMounts, volumeMountName,
path.Join(quarkusDevConfigMountPath, workflowResCM.WorkflowPath))
resourceVolumes =
kubeutil.VolumeAddVolumeProjectionConfigMap(resourceVolumes,
workflowResCM.ConfigMap.Name, volumeMountName)
}
diff --git a/controllers/profiles/dev/profile_dev_test.go
b/controllers/profiles/dev/profile_dev_test.go
index c9ad1860..d5a6a379 100644
--- a/controllers/profiles/dev/profile_dev_test.go
+++ b/controllers/profiles/dev/profile_dev_test.go
@@ -22,6 +22,8 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ kubeutil
"github.com/kiegroup/kogito-serverless-operator/utils/kubernetes"
+
"github.com/kiegroup/kogito-serverless-operator/controllers/profiles/common"
operatorapi
"github.com/kiegroup/kogito-serverless-operator/api/v1alpha08"
@@ -296,12 +298,11 @@ func Test_newDevProfileWithExternalConfigMaps(t
*testing.T) {
assert.Equal(t, 2, len(deployment.Spec.Template.Spec.Volumes))
sortVolumeMounts(&deployment.Spec.Template.Spec.Containers[0])
- wd := deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0]
- extCamel := deployment.Spec.Template.Spec.Containers[0].VolumeMounts[1]
+ wd := deployment.Spec.Template.Spec.Containers[0].VolumeMounts[1]
+ extCamel := deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0]
assert.Equal(t, configMapResourcesVolumeName, wd.Name)
assert.Equal(t, quarkusDevConfigMountPath, wd.MountPath)
- assert.Equal(t, configMapExternalResourcesVolumeNamePrefix+"routes",
extCamel.Name)
assert.Equal(t, extCamel.MountPath, quarkusDevConfigMountPath+"/routes")
cmData[camelYamlRouteFileName] = yamlRoute
@@ -323,8 +324,7 @@ func Test_newDevProfileWithExternalConfigMaps(t *testing.T)
{
assert.Equal(t, 2, len(deployment.Spec.Template.Spec.Volumes))
sortVolumeMounts(&deployment.Spec.Template.Spec.Containers[0])
- extCamelRouteOne :=
deployment.Spec.Template.Spec.Containers[0].VolumeMounts[1]
- assert.Equal(t, configMapExternalResourcesVolumeNamePrefix+"routes",
extCamelRouteOne.Name)
+ extCamelRouteOne :=
deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0]
assert.Equal(t, quarkusDevConfigMountPath+"/routes",
extCamelRouteOne.MountPath)
workflow.Status.Manager().MarkTrue(api.RunningConditionType)
@@ -368,6 +368,29 @@ func Test_newDevProfileWithExternalConfigMaps(t
*testing.T) {
assert.Equal(t, wd.MountPath, quarkusDevConfigMountPath)
}
+func Test_VolumeWithCapitalizedPaths(t *testing.T) {
+ configMap := &v1.ConfigMap{}
+ test.GetKubernetesResource(test.SonataFlowGreetingsStaticFilesConfig,
configMap)
+ configMap.Namespace = t.Name()
+ workflow :=
test.GetSonataFlow(test.SonataFlowGreetingsWithStaticResourcesCR, t.Name())
+
+ client := test.NewKogitoClientBuilder().WithRuntimeObjects(workflow,
configMap).WithStatusSubresource(workflow, configMap).Build()
+
+ devReconciler := NewProfileReconciler(client)
+
+ result, err := devReconciler.Reconcile(context.TODO(), workflow)
+ assert.NoError(t, err)
+ assert.NotNil(t, result)
+
+ deployment := test.MustGetDeployment(t, client, workflow)
+ assert.NotNil(t, deployment)
+
+ container, _ :=
kubeutil.GetContainerByName(operatorapi.DefaultContainerName,
&deployment.Spec.Template.Spec)
+ // properties, definitions, and the capitalized value
+ assert.Len(t, container.VolumeMounts, 2)
+ assert.Len(t, deployment.Spec.Template.Spec.Volumes, 2)
+}
+
func sortVolumeMounts(container *v1.Container) {
sort.SliceStable(container.VolumeMounts, func(i, j int) bool {
return container.VolumeMounts[i].Name <
container.VolumeMounts[j].Name
diff --git a/controllers/profiles/dev/states_dev.go
b/controllers/profiles/dev/states_dev.go
index df7cd329..2c30e0b3 100644
--- a/controllers/profiles/dev/states_dev.go
+++ b/controllers/profiles/dev/states_dev.go
@@ -38,8 +38,7 @@ import (
const (
configMapResourcesVolumeName = "resources"
- configMapExternalResourcesVolumeNamePrefix =
configMapResourcesVolumeName + "-"
-
+ configMapExternalResourcesVolumeNamePrefix = "res-"
// quarkusDevConfigMountPath mount path for application properties file
in the Workflow Quarkus Application
// See:
https://quarkus.io/guides/config-reference#application-properties-file
quarkusDevConfigMountPath =
"/home/kogito/serverless-workflow-project/src/main/resources"
diff --git a/test/testdata/sonataflow.org_v1alpha08_sonataflow-metainf.yaml
b/test/testdata/sonataflow.org_v1alpha08_sonataflow-metainf.yaml
new file mode 100644
index 00000000..3b2c7a32
--- /dev/null
+++ b/test/testdata/sonataflow.org_v1alpha08_sonataflow-metainf.yaml
@@ -0,0 +1,62 @@
+# Copyright 2023 Red Hat, Inc. and/or its affiliates
+#
+# Licensed 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.
+
+apiVersion: sonataflow.org/v1alpha08
+kind: SonataFlow
+metadata:
+ name: greeting
+ annotations:
+ sonataflow.org/description: Greeting example on k8s!
+ sonataflow.org/version: 0.0.1
+ sonataflow.org/profile: dev
+spec:
+ resources:
+ configMaps:
+ - configMap:
+ name: greetings-staticfiles
+ workflowPath: META-INF/resources
+ flow:
+ start: ChooseOnLanguage
+ functions:
+ - name: greetFunction
+ type: custom
+ operation: sysout
+ states:
+ - name: ChooseOnLanguage
+ type: switch
+ dataConditions:
+ - condition: "${ .language == \"English\" }"
+ transition: GreetInEnglish
+ - condition: "${ .language == \"Spanish\" }"
+ transition: GreetInSpanish
+ defaultCondition: GreetInEnglish
+ - name: GreetInEnglish
+ type: inject
+ data:
+ greeting: "Hello from JSON Workflow, "
+ transition: GreetPerson
+ - name: GreetInSpanish
+ type: inject
+ data:
+ greeting: "Saludos desde JSON Workflow, "
+ transition: GreetPerson
+ - name: GreetPerson
+ type: operation
+ actions:
+ - name: greetAction
+ functionRef:
+ refName: greetFunction
+ arguments:
+ message: ".greeting+.name"
+ end: true
diff --git a/test/testdata/v1_configmap_greetings_staticfiles.yaml
b/test/testdata/v1_configmap_greetings_staticfiles.yaml
new file mode 100644
index 00000000..ccfe04c7
--- /dev/null
+++ b/test/testdata/v1_configmap_greetings_staticfiles.yaml
@@ -0,0 +1,24 @@
+# Copyright 2023 Red Hat, Inc. and/or its affiliates
+#
+# Licensed 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.
+
+kind: ConfigMap
+apiVersion: v1
+metadata:
+ name: greetings-staticfiles
+data:
+ index.html: |-
+ <html>
+ <title>Greetings!</title>
+ <body>Greetings stranger!</body>
+ </html>
\ No newline at end of file
diff --git a/test/yaml.go b/test/yaml.go
index f5fe97bc..ed01f7c1 100644
--- a/test/yaml.go
+++ b/test/yaml.go
@@ -24,6 +24,7 @@ import (
"github.com/davecgh/go-spew/spew"
"k8s.io/klog/v2"
+ "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/kiegroup/kogito-serverless-operator/api"
@@ -40,8 +41,10 @@ const (
sonataFlowOrderProcessingFolder = "order-processing"
sonataFlowSampleYamlCR =
"sonataflow.org_v1alpha08_sonataflow.yaml"
SonataFlowGreetingsWithDataInputSchemaCR =
"sonataflow.org_v1alpha08_sonataflow_greetings_datainput.yaml"
+ SonataFlowGreetingsWithStaticResourcesCR =
"sonataflow.org_v1alpha08_sonataflow-metainf.yaml"
SonataFlowSimpleOpsYamlCR =
"sonataflow.org_v1alpha08_sonataflow-simpleops.yaml"
SonataFlowGreetingsDataInputSchemaConfig =
"v1_configmap_greetings_datainput.yaml"
+ SonataFlowGreetingsStaticFilesConfig =
"v1_configmap_greetings_staticfiles.yaml"
sonataFlowPlatformYamlCR =
"sonataflow.org_v1alpha08_sonataflowplatform.yaml"
sonataFlowPlatformWithCacheMinikubeYamlCR =
"sonataflow.org_v1alpha08_sonataflowplatform_withCache_minikube.yaml"
sonataFlowPlatformForOpenshift =
"sonataflow.org_v1alpha08_sonataflowplatform_openshift.yaml"
@@ -54,9 +57,16 @@ const (
var projectDir = ""
-func getSonataFlow(testFile, namespace string) *operatorapi.SonataFlow {
+func GetSonataFlow(testFile, namespace string) *operatorapi.SonataFlow {
ksw := &operatorapi.SonataFlow{}
+ GetKubernetesResource(testFile, ksw)
+ klog.V(log.D).InfoS("Successfully read KSW", "ksw", spew.Sprint(ksw))
+ ksw.Namespace = namespace
+ return ksw
+}
+
+func GetKubernetesResource(testFile string, resource client.Object) {
yamlFile, err := os.ReadFile(path.Join(getTestDataDir(), testFile))
if err != nil {
klog.V(log.E).ErrorS(err, "yamlFile.Get")
@@ -64,14 +74,11 @@ func getSonataFlow(testFile, namespace string)
*operatorapi.SonataFlow {
}
// Important: Here we are reading the CR deployment file from a given
path and creating an &operatorapi.SonataFlow struct
- err = yaml.NewYAMLOrJSONDecoder(bytes.NewReader(yamlFile),
100).Decode(ksw)
+ err = yaml.NewYAMLOrJSONDecoder(bytes.NewReader(yamlFile),
100).Decode(resource)
if err != nil {
klog.V(log.E).ErrorS(err, "Unmarshal")
panic(err)
}
- klog.V(log.D).InfoS("Successfully read KSW", "ksw", spew.Sprint(ksw))
- ksw.Namespace = namespace
- return ksw
}
func getSonataFlowPlatform(testFile string) *operatorapi.SonataFlowPlatform {
@@ -149,7 +156,7 @@ func GetSonataFlowBuilderConfig(namespace string)
*corev1.ConfigMap {
}
func GetBaseSonataFlow(namespace string) *operatorapi.SonataFlow {
- return getSonataFlow(sonataFlowSampleYamlCR, namespace)
+ return GetSonataFlow(sonataFlowSampleYamlCR, namespace)
}
func GetBaseSonataFlowWithDevProfile(namespace string) *operatorapi.SonataFlow
{
@@ -165,7 +172,7 @@ func GetBaseSonataFlowWithProdProfile(namespace string)
*operatorapi.SonataFlow
}
func GetBaseSonataFlowWithProdOpsProfile(namespace string)
*operatorapi.SonataFlow {
- workflow := getSonataFlow(SonataFlowSimpleOpsYamlCR, namespace)
+ workflow := GetSonataFlow(SonataFlowSimpleOpsYamlCR, namespace)
return workflow
}
diff --git a/utils/kubernetes/naming.go b/utils/kubernetes/naming.go
new file mode 100644
index 00000000..9f78df37
--- /dev/null
+++ b/utils/kubernetes/naming.go
@@ -0,0 +1,49 @@
+// Copyright 2023 Red Hat, Inc. and/or its affiliates
+//
+// Licensed 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.
+
+package kubernetes
+
+import (
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/api/validation"
+ "k8s.io/apimachinery/pkg/util/rand"
+)
+
+const dns1035MaxChar int = 63
+
+// SafeDNS1035 generates a safe encoded string based on "s" with the given
prefix.
+// Ideally used with internal generated names.
+//
+// See
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#rfc-1035-label-names
+func SafeDNS1035(prefix, s string) (string, error) {
+ safeNaming := prefix + rand.SafeEncodeString(s)
+ if len(safeNaming) > dns1035MaxChar {
+ safeNaming = safeNaming[:dns1035MaxChar]
+ }
+ errMsgs := validation.NameIsDNS1035Label(safeNaming, false)
+ if len(errMsgs) > 0 {
+ return "", fmt.Errorf("failed to generate a safe name for %s
with prefix %s: %v", s, prefix, errMsgs)
+ }
+ return safeNaming, nil
+}
+
+// MustSafeDNS1035 see SafeDNS1035. Use this function only if you control the
prefix.
+func MustSafeDNS1035(prefix, s string) string {
+ name, err := SafeDNS1035(prefix, s)
+ if err != nil {
+ panic(err)
+ }
+ return name
+}
diff --git a/utils/strings.go b/utils/strings.go
index 61ba1a61..41b785f7 100644
--- a/utils/strings.go
+++ b/utils/strings.go
@@ -15,8 +15,6 @@
package utils
import (
- "os"
- "path"
"strings"
)
@@ -33,8 +31,3 @@ func RemoveKnownExtension(fileName, extension string) string {
}
return fileName
}
-
-// PathToString replaces the PathSeparator from a given path.
-func PathToString(pathRef string) string {
- return strings.ReplaceAll(path.Clean(pathRef),
string(os.PathSeparator), "")
-}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]