Skip to content

Commit

Permalink
Merge pull request #4 from treblereel/405-tests
Browse files Browse the repository at this point in the history
405 tests
  • Loading branch information
treblereel committed Oct 7, 2024
2 parents 8f97b82 + 6c2f552 commit e96527e
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 12 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,5 @@ delete-cluster: install-kind
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
13 changes: 9 additions & 4 deletions internal/controller/validation/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)

func KindRegistryName(ctx context.Context) (string, error) {
var kindRegistryName = func(ctx context.Context) (string, error) {
config := corev1.ConfigMap{}
err := utils.GetClient().Get(ctx, client.ObjectKey{Namespace: "kube-public", Name: "local-registry-hosting"}, &config)
if err == nil {
Expand All @@ -43,6 +43,7 @@ func KindRegistryName(ctx context.Context) (string, error) {
}

func checkUrlHasPrefix(input string) bool {
input = strings.ToLower(input)
prefixes := []string{"http://", "https://", "docker://"}
for _, prefix := range prefixes {
if strings.HasPrefix(input, prefix) {
Expand All @@ -69,7 +70,7 @@ func hostAndPortFromUri(input string) (string, string, error) {

// check if host is ip address
if net.ParseIP(host) == nil {
hosts, err := net.LookupIP(host)
hosts, err := resolve(host)
if err != nil {
return "", "", fmt.Errorf("Failed to resolve domain: %v\n", err)
}
Expand All @@ -82,8 +83,8 @@ func hostAndPortFromUri(input string) (string, string, error) {
return host, port, nil
}

func ImageStoredInKindRegistry(ctx context.Context, image string) (bool, string, error) {
kindRegistryHostAndPort, err := KindRegistryName(ctx)
func imageStoredInKindRegistry(ctx context.Context, image string) (bool, string, error) {
kindRegistryHostAndPort, err := kindRegistryName(ctx)
if err != nil {
return false, "", fmt.Errorf("Failed to get kind registry name: %v\n", err)
}
Expand All @@ -110,3 +111,7 @@ func getipv4(ips []net.IP) (string, error) {
}
return "", fmt.Errorf("No ipv4 address found")
}

var resolve = func(host string) ([]net.IP, error) {
return net.LookupIP(host)
}
118 changes: 118 additions & 0 deletions internal/controller/validation/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// 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"
"net"
"testing"
)

func TestCheckUrlHasPrefix(t *testing.T) {
tests := []struct {
input string
expected bool
}{
{"http://example.com", true},
{"https://example.com", true},
{"docker://my-image", true},

{"example.com", false},
{"127.0.0.1", false},
{"my-image", false},

{"HTTP://example.com", true},
{"HTTPS://example.com", true},
{"DOCKER://my-image", true},
}

for _, test := range tests {
result := checkUrlHasPrefix(test.input)
if result != test.expected {
t.Errorf("checkUrlHasPrefix(%q) = %v; expected %v", test.input, result, test.expected)
}
}
}

func TestHostAndPortFromUri(t *testing.T) {
tests := []struct {
input string
expectedHost string
expectedPort string
expectingError bool
}{
{"http://192.168.1.1:8080", "192.168.1.1", "8080", false},
{"https://192.168.1.1:8080", "192.168.1.1", "8080", false},
{"docker://192.168.1.1:8080", "192.168.1.1", "8080", false},
{"http://localhost:5000", "127.0.0.1", "5000", false},
{"https://localhost:5000", "127.0.0.1", "5000", false},
{"docker://localhost:5000", "127.0.0.1", "5000", false},
{"localhost:5000", "127.0.0.1", "5000", false},
{"ftp://example.com", "", "", true},
{"invalid_url", "", "", true},
}

for _, test := range tests {
host, port, err := hostAndPortFromUri(test.input)

if (err != nil) != test.expectingError {
t.Errorf("hostAndPortFromUri(%q) error = %v, expected error = %v", test.input, err, test.expectingError)
}
if host != test.expectedHost || port != test.expectedPort {
t.Errorf("hostAndPortFromUri(%q) = (%q, %q), expected (%q, %q)", test.input, host, port, test.expectedHost, test.expectedPort)
}
}
}

func TestImageStoredInKindRegistry(t *testing.T) {
originalKindRegistryNameFunc := kindRegistryName
originalResolveFunc := resolve
defer func() {
kindRegistryName = originalKindRegistryNameFunc
resolve = originalResolveFunc
}()

tests := []struct {
image string
expectedResult bool
expectedHost string
resolvedIp string
expectingError bool
}{
{"kind-registry:5000/my-image", true, "172.18.0.4:5000", "172.18.0.4", false},
{"172.18.0.4:5000/my-image", true, "172.18.0.4:5000", "0.0.0.0", false},
{"docker.io/my-image", false, "", "1.1.1.1", false},
{"172.18.0.4:6000/my-image", false, "", "0.0.0.0", false},
}

for _, test := range tests {
kindRegistryName = func(ctx context.Context) (string, error) {
return "172.18.0.4:5000", nil
}
resolve = func(host string) ([]net.IP, error) {
return []net.IP{net.ParseIP(test.resolvedIp)}, nil
}

result, hostAndPort, err := imageStoredInKindRegistry(context.Background(), test.image)

if (err != nil) != test.expectingError {
t.Errorf("ImageStoredInKindRegistry(%q) error = %v, expected error = %v", test.image, err, test.expectingError)
}

if result != test.expectedResult || hostAndPort != test.expectedHost {
t.Errorf("ImageStoredInKindRegistry(%q) = (%v, %q), expected (%v, %q)", test.image, result, hostAndPort, test.expectedResult, test.expectedHost)
}
}
}
2 changes: 1 addition & 1 deletion internal/controller/validation/image_url_sanitizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
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)
isInKindRegistry, kindRegistryUrl, err := imageStoredInKindRegistry(ctx, sonataflow.Spec.PodTemplate.Container.Image)
if err != nil {
return err
}
Expand Down
6 changes: 1 addition & 5 deletions internal/controller/validation/image_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func NewImageValidator() Validator {
}

func validateImage(ctx context.Context, sonataflow *operatorapi.SonataFlow) (bool, error) {
isInKindRegistry, _, err := ImageStoredInKindRegistry(ctx, sonataflow.Spec.PodTemplate.Container.Image)
isInKindRegistry, _, err := imageStoredInKindRegistry(ctx, sonataflow.Spec.PodTemplate.Container.Image)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -82,8 +82,6 @@ func validateImage(ctx context.Context, sonataflow *operatorapi.SonataFlow) (boo
}

func remoteImage(sonataflow *operatorapi.SonataFlow) (v1.Image, error) {
fmt.Println("remoteImage")

imageRef, err := name.ParseReference(sonataflow.Spec.PodTemplate.Container.Image)
if err != nil {
return nil, err
Expand All @@ -97,8 +95,6 @@ func remoteImage(sonataflow *operatorapi.SonataFlow) (v1.Image, error) {
}

func kindRegistryImage(sonataflow *operatorapi.SonataFlow) (v1.Image, error) {
fmt.Println("kindRegistryImage")

transportOptions := []remote.Option{
remote.WithTransport(&http.Transport{
Proxy: http.ProxyFromEnvironment,
Expand Down
43 changes: 43 additions & 0 deletions test/e2e/workflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,49 @@ var _ = Describe("Workflow Non-Persistence Use Cases :: ", Label("flows-non-pers
}, 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", filepath.Join(projectDir,
"test/testdata/"+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", filepath.Join(projectDir,
"test/testdata/"+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", filepath.Join(projectDir,
"test/testdata/"+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", filepath.Join(projectDir,
"test/testdata/"+test.SonataFlowSimpleOpsYamlCRImageContainsBrokenWorkflow), "-n", targetNamespace)
_, err := utils.Run(cmd)
return err
}, 3*time.Minute, time.Second).Should(Succeed())
})
})

})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 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
18 changes: 18 additions & 0 deletions test/testdata/workflow/docker-image/broken-workflow.sw.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"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
}
]
}
7 changes: 5 additions & 2 deletions test/yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,11 @@ const (
sonataFlowBuilderConfig = "sonataflow-operator-builder-config_v1_configmap.yaml"
sonataFlowBuildSucceed = "sonataflow.org_v1alpha08_sonataflowbuild.yaml"
knativeDefaultBrokerCR = "knative_default_broker.yaml"
e2eSamples = "test/testdata/"
manifestsPath = "bundle/manifests/"

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/"
)

var projectDir = ""
Expand Down

0 comments on commit e96527e

Please sign in to comment.