From 1934daec97da40b2e7590aebc3b593b45801078b Mon Sep 17 00:00:00 2001 From: Dmitrii Tikhomirov Date: Tue, 8 Oct 2024 18:32:47 -0700 Subject: [PATCH] Aligned with the upstream changes, kind-registry hack removed and workflow search criteria have been relaxed --- .github/workflows/e2e.yml | 4 -- Makefile | 7 -- ...-operator-builder-config_v1_configmap.yaml | 1 + .../manager/SonataFlow-Builder.containerfile | 1 + .../validation/image_url_sanitizer.go | 69 ------------------- .../controller/validation/image_validator.go | 20 +++--- internal/controller/validation/validator.go | 2 +- operator.yaml | 1 + test/e2e/workflow_test.go | 39 ----------- ...ow-simpleops-broken-workflow-in-image.yaml | 35 ---------- ...taflow-simpleops-no-workflow-in-image.yaml | 35 ---------- ...ow.org_v1alpha08_sonataflow-simpleops.yaml | 2 +- .../testdata/workflow/docker-image/Dockerfile | 17 ----- .../docker-image/broken-workflow.sw.json | 18 ----- .../workflow/docker-image/workflow.sw.json | 18 ----- test/yaml.go | 7 +- 16 files changed, 19 insertions(+), 257 deletions(-) delete mode 100644 internal/controller/validation/image_url_sanitizer.go delete mode 100644 test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops-broken-workflow-in-image.yaml delete mode 100644 test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops-no-workflow-in-image.yaml delete mode 100644 test/testdata/workflow/docker-image/Dockerfile delete mode 100644 test/testdata/workflow/docker-image/broken-workflow.sw.json delete mode 100644 test/testdata/workflow/docker-image/workflow.sw.json diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index a722914eb..ec598cc88 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -76,10 +76,6 @@ jobs: make container-build BUILDER=docker IMG=${{ env.OPERATOR_IMAGE_NAME }} kind load docker-image ${{ env.OPERATOR_IMAGE_NAME }} - - name: Build and Install SonataFlow worfklow test images - run: | - make workflow_test_image_build-and-push - - name: Check Pods run: | kubectl version diff --git a/Makefile b/Makefile index 8094bcdc0..12503c80f 100644 --- a/Makefile +++ b/Makefile @@ -453,10 +453,3 @@ deploy-knative: .PHONY: delete-cluster delete-cluster: install-kind kind delete cluster && $(BUILDER) rm -f kind-registry - -.PHONY: workflow_test_image_build-and-push -workflow_test_image_build-and-push: - docker build -t localhost:5001/testimage/sonataflow-minimal-example:0.1 ./test/testdata/workflow/docker-image/ - docker push localhost:5001/testimage/sonataflow-minimal-example:0.1 - docker build --build-arg WORKFLOW_FILE=broken-workflow.sw.json -t localhost:5001/testimage/sonataflow-minimal-example-broken:0.1 ./test/testdata/workflow/docker-image/ - docker push localhost:5001/testimage/sonataflow-minimal-example-broken:0.1 diff --git a/bundle/manifests/sonataflow-operator-builder-config_v1_configmap.yaml b/bundle/manifests/sonataflow-operator-builder-config_v1_configmap.yaml index eba99337c..4c53cd011 100644 --- a/bundle/manifests/sonataflow-operator-builder-config_v1_configmap.yaml +++ b/bundle/manifests/sonataflow-operator-builder-config_v1_configmap.yaml @@ -29,6 +29,7 @@ data: COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/*.jar /deployments/ COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/app/ /deployments/app/ COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/quarkus/ /deployments/quarkus/ + COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/src/main/resources/*.sw.* /deployments/app/ EXPOSE 8080 USER 185 diff --git a/config/manager/SonataFlow-Builder.containerfile b/config/manager/SonataFlow-Builder.containerfile index e779a8433..8dbd5bafb 100644 --- a/config/manager/SonataFlow-Builder.containerfile +++ b/config/manager/SonataFlow-Builder.containerfile @@ -25,6 +25,7 @@ COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/ COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/*.jar /deployments/ COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/app/ /deployments/app/ COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/quarkus/ /deployments/quarkus/ +COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/src/main/resources/*.sw.* /deployments/app/ EXPOSE 8080 USER 185 diff --git a/internal/controller/validation/image_url_sanitizer.go b/internal/controller/validation/image_url_sanitizer.go deleted file mode 100644 index 364ecdc90..000000000 --- a/internal/controller/validation/image_url_sanitizer.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2024 Apache Software Foundation (ASF) -// -// 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 validation - -import ( - "context" - "fmt" - "net" - - operatorapi "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08" - "github.com/google/go-containerregistry/pkg/name" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type imageUrlSanitizer struct{} - -func (v *imageUrlSanitizer) Validate(ctx context.Context, client client.Client, sonataflow *operatorapi.SonataFlow, req ctrl.Request) error { - isInKindRegistry, kindRegistryUrl, err := imageStoredInKindRegistry(ctx, sonataflow.Spec.PodTemplate.Container.Image) - if err != nil { - return err - } - if isInKindRegistry { - ref, err := name.ParseReference(sonataflow.Spec.PodTemplate.Container.Image) - if err != nil { - return fmt.Errorf("Unable to parse image uri: %w", err) - } - // host or ip address and port - hostAndPortName := ref.Context().RegistryStr() - - host, _, err := net.SplitHostPort(hostAndPortName) - if err != nil { - return fmt.Errorf("Unable to parse image uri: %w", err) - } - // check if host is not ip address, - if net.ParseIP(host) == nil { - // take part after port - var result string - // if tag isn't present, it's latest - if tag, ok := ref.(name.Tag); ok { - result = fmt.Sprintf("%s/%s:%s", kindRegistryUrl, ref.Context().RepositoryStr(), tag.TagStr()) - } else { - result = fmt.Sprintf("%s/%s", kindRegistryUrl, ref.Context().RepositoryStr()) - } - sonataflow.Spec.PodTemplate.Container.Image = result - if err := client.Update(ctx, sonataflow); err != nil { - return fmt.Errorf("failed to update SonataFlow object: %w", err) - } - } - } - - return nil -} - -func NewImageUrlSanitizer() Validator { - return &imageUrlSanitizer{} -} diff --git a/internal/controller/validation/image_validator.go b/internal/controller/validation/image_validator.go index fac4d73ee..c23bb9415 100644 --- a/internal/controller/validation/image_validator.go +++ b/internal/controller/validation/image_validator.go @@ -21,6 +21,10 @@ import ( "fmt" "io" "net/http" + "regexp" + + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/serverlessworkflow/sdk-go/v2/model" operatorapi "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08" "github.com/google/go-cmp/cmp" @@ -34,7 +38,7 @@ import ( type imageValidator struct{} -const workflowName = "deployments/app/workflow.sw.json" +var workflowPathRegex = regexp.MustCompile(`^deployments/app/[^/]+\.sw\.(json|yaml)$`) func (v *imageValidator) Validate(ctx context.Context, client client.Client, sonataflow *operatorapi.SonataFlow, req ctrl.Request) error { equals, err := validateImage(ctx, sonataflow) @@ -68,7 +72,7 @@ func validateImage(ctx context.Context, sonataflow *operatorapi.SonataFlow) (boo return false, err } - reader, err := readWorkflowSpecLayer(ref, workflowName) + reader, err := readWorkflowSpecLayer(ref) if err != nil { return false, err } @@ -78,7 +82,7 @@ func validateImage(ctx context.Context, sonataflow *operatorapi.SonataFlow) (boo return false, err } - return cmp.Equal(workflowDockerImage, sonataflow.Spec.Flow), nil + return cmp.Equal(workflowDockerImage, sonataflow.Spec.Flow, cmpopts.IgnoreUnexported(model.Transition{})), nil } func remoteImage(sonataflow *operatorapi.SonataFlow) (v1.Image, error) { @@ -114,23 +118,23 @@ func kindRegistryImage(sonataflow *operatorapi.SonataFlow) (v1.Image, error) { return ref, nil } -func readWorkflowSpecLayer(image v1.Image, workflow string) (*tar.Reader, error) { +func readWorkflowSpecLayer(image v1.Image) (*tar.Reader, error) { layers, err := image.Layers() if err != nil { return nil, err } for i := len(layers) - 1; i >= 0; i-- { - if reader, err := findWorkflowSpecLayer(layers[i], workflow); err == nil && reader != nil { + if reader, err := findWorkflowSpecLayer(layers[i]); err == nil && reader != nil { return reader, nil } else if err != nil { return nil, err } } - return nil, fmt.Errorf("file not found %s in docker image", workflow) + return nil, fmt.Errorf("Workflow definition was not found in the Docker image") } -func findWorkflowSpecLayer(layer v1.Layer, workflow string) (*tar.Reader, error) { +func findWorkflowSpecLayer(layer v1.Layer) (*tar.Reader, error) { uncompressedLayer, err := layer.Uncompressed() if err != nil { return nil, fmt.Errorf("failed to get uncompressed layer: %v", err) @@ -147,7 +151,7 @@ func findWorkflowSpecLayer(layer v1.Layer, workflow string) (*tar.Reader, error) return nil, fmt.Errorf("error reading tar: %v", err) } - if header.Typeflag == '0' && header.Name == workflow { + if header.Typeflag == '0' && workflowPathRegex.MatchString(header.Name) { return tarReader, nil } } diff --git a/internal/controller/validation/validator.go b/internal/controller/validation/validator.go index 36e6e83aa..bbcfe1fc7 100644 --- a/internal/controller/validation/validator.go +++ b/internal/controller/validation/validator.go @@ -22,7 +22,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -var validators = []Validator{NewImageValidator(), NewImageUrlSanitizer()} +var validators = []Validator{NewImageValidator()} type Validator interface { Validate(ctx context.Context, client client.Client, sonataflow *operatorapi.SonataFlow, req ctrl.Request) error diff --git a/operator.yaml b/operator.yaml index 27391c7c6..1e2e72689 100644 --- a/operator.yaml +++ b/operator.yaml @@ -28206,6 +28206,7 @@ data: COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/*.jar /deployments/ COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/app/ /deployments/app/ COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/quarkus/ /deployments/quarkus/ + COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/src/main/resources/*.sw.* /deployments/app/ EXPOSE 8080 USER 185 diff --git a/test/e2e/workflow_test.go b/test/e2e/workflow_test.go index 9eda94634..e7e0a1aa4 100644 --- a/test/e2e/workflow_test.go +++ b/test/e2e/workflow_test.go @@ -137,45 +137,6 @@ var _ = Describe("Workflow Non-Persistence Use Cases :: ", Label("flows-ephemera }, 3*time.Minute, time.Second).Should(Succeed()) }) - It("should not deploy the Simple Workflow because image contains no workflow definition", func() { - By("creating an instance of the SonataFlow Operand(CR)") - EventuallyWithOffset(1, func() error { - cmd := exec.Command("kubectl", "apply", "-f", test.GetPathFromDataDirectory(test.SonataFlowSimpleOpsYamlCRImageContainsNoWorkflow), "-n", targetNamespace) - _, err := utils.Run(cmd) - return err - }, 3*time.Minute, time.Second).Should(Succeed()) - - By("verifying that the workflow is not in a running state within one minute") - ConsistentlyWithOffset(1, func() bool { - return verifyWorkflowIsInRunningState("simple", targetNamespace) - }, 1*time.Minute, 10*time.Second).Should(BeFalse(), "Workflow unexpectedly reached the running state") - - EventuallyWithOffset(1, func() error { - cmd := exec.Command("kubectl", "delete", "-f", test.GetPathFromDataDirectory(test.SonataFlowSimpleOpsYamlCRImageContainsNoWorkflow), "-n", targetNamespace) - _, err := utils.Run(cmd) - return err - }, 3*time.Minute, time.Second).Should(Succeed()) - }) - - It("should not deploy the Simple Workflow because image contains broken (not equals to flow from yaml) workflow definition", func() { - By("creating an instance of the SonataFlow Operand(CR)") - EventuallyWithOffset(1, func() error { - cmd := exec.Command("kubectl", "apply", "-f", test.GetPathFromDataDirectory(test.SonataFlowSimpleOpsYamlCRImageContainsBrokenWorkflow), "-n", targetNamespace) - _, err := utils.Run(cmd) - return err - }, 3*time.Minute, time.Second).Should(Succeed()) - - By("verifying that the workflow is not in a running state within one minute") - ConsistentlyWithOffset(1, func() bool { - return verifyWorkflowIsInRunningState("simple", targetNamespace) - }, 1*time.Minute, 10*time.Second).Should(BeFalse(), "Workflow unexpectedly reached the running state") - - EventuallyWithOffset(1, func() error { - cmd := exec.Command("kubectl", "delete", "-f", test.GetPathFromDataDirectory(test.SonataFlowSimpleOpsYamlCRImageContainsBrokenWorkflow), "-n", targetNamespace) - _, err := utils.Run(cmd) - return err - }, 3*time.Minute, time.Second).Should(Succeed()) - }) }) }) diff --git a/test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops-broken-workflow-in-image.yaml b/test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops-broken-workflow-in-image.yaml deleted file mode 100644 index a0d6dcc6a..000000000 --- a/test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops-broken-workflow-in-image.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# 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: simple - annotations: - sonataflow.org/description: Simple example on k8s! - sonataflow.org/version: 0.0.1 - labels: - test: test -spec: - podTemplate: - container: - image: testimage/sonataflow-minimal-example-broken:0.1 - flow: - start: HelloWorld - states: - - name: HelloWorld - type: inject - data: - message: Hello World - end: true diff --git a/test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops-no-workflow-in-image.yaml b/test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops-no-workflow-in-image.yaml deleted file mode 100644 index b1a11d631..000000000 --- a/test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops-no-workflow-in-image.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# 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: simple - annotations: - sonataflow.org/description: Simple example on k8s! - sonataflow.org/version: 0.0.1 - labels: - test: test -spec: - podTemplate: - container: - image: quay.io/kiegroup/sonataflow-minimal-example:latest - flow: - start: HelloWorld - states: - - name: HelloWorld - type: inject - data: - message: Hello World - end: true diff --git a/test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops.yaml b/test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops.yaml index e7015eefa..6b474257b 100644 --- a/test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops.yaml +++ b/test/testdata/sonataflow.org_v1alpha08_sonataflow-simpleops.yaml @@ -26,7 +26,7 @@ spec: container: # TODO use docker.io/apache/incubator-kie-sonataflow-minimal-example:latest # when we complete https://github.com/apache/incubator-kie-kogito-serverless-operator/issues/504 - image: kind-registry:5000/testimage/sonataflow-minimal-example:0.1 + image: quay.io/kiegroup/sonataflow-minimal-example:latest flow: start: HelloWorld states: diff --git a/test/testdata/workflow/docker-image/Dockerfile b/test/testdata/workflow/docker-image/Dockerfile deleted file mode 100644 index 9b21f40fe..000000000 --- a/test/testdata/workflow/docker-image/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2024 Apache Software Foundation (ASF) -# -# 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. - -FROM quay.io/kiegroup/sonataflow-minimal-example:latest -ARG WORKFLOW_FILE=workflow.sw.json -COPY ${WORKFLOW_FILE} /deployments/app/workflow.sw.json diff --git a/test/testdata/workflow/docker-image/broken-workflow.sw.json b/test/testdata/workflow/docker-image/broken-workflow.sw.json deleted file mode 100644 index 3ef8543c5..000000000 --- a/test/testdata/workflow/docker-image/broken-workflow.sw.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "hello", - "version": "1.0", - "specVersion": "0.8.0", - "name": "Hello World", - "description": "Description", - "start": "HelloWorld", - "states": [ - { - "name": "HelloWorld", - "type": "inject", - "data": { - "message": "Hello World" - }, - "end": true - } - ] -} diff --git a/test/testdata/workflow/docker-image/workflow.sw.json b/test/testdata/workflow/docker-image/workflow.sw.json deleted file mode 100644 index 3ef8543c5..000000000 --- a/test/testdata/workflow/docker-image/workflow.sw.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id": "hello", - "version": "1.0", - "specVersion": "0.8.0", - "name": "Hello World", - "description": "Description", - "start": "HelloWorld", - "states": [ - { - "name": "HelloWorld", - "type": "inject", - "data": { - "message": "Hello World" - }, - "end": true - } - ] -} diff --git a/test/yaml.go b/test/yaml.go index c942ee2c5..276f9b9b7 100644 --- a/test/yaml.go +++ b/test/yaml.go @@ -59,11 +59,8 @@ const ( sonataFlowBuilderConfig = "sonataflow-operator-builder-config_v1_configmap.yaml" sonataFlowBuildSucceed = "sonataflow.org_v1alpha08_sonataflowbuild.yaml" knativeDefaultBrokerCR = "knative_default_broker.yaml" - - SonataFlowSimpleOpsYamlCRImageContainsNoWorkflow = "sonataflow.org_v1alpha08_sonataflow-simpleops-no-workflow-in-image.yaml" - SonataFlowSimpleOpsYamlCRImageContainsBrokenWorkflow = "sonataflow.org_v1alpha08_sonataflow-simpleops-broken-workflow-in-image.yaml" - e2eSamples = "test/testdata/" - manifestsPath = "bundle/manifests/" + e2eSamples = "test/testdata/" + manifestsPath = "bundle/manifests/" ) var projectDir = ""