Skip to content

Commit

Permalink
Merge pull request #3 from treblereel/405-ci
Browse files Browse the repository at this point in the history
build and install workflow images
  • Loading branch information
treblereel authored Oct 6, 2024
2 parents 0eba579 + 78039bb commit b838cd2
Show file tree
Hide file tree
Showing 16 changed files with 284 additions and 25 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ 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
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,12 @@ deploy-knative:
kubectl apply -f ./test/testdata/knative_serving_eventing.yaml
kubectl wait --for=condition=Ready=True KnativeServing/knative-serving -n knative-serving --timeout=$(TIMEOUT_SECS)
kubectl wait --for=condition=Ready=True KnativeEventing/knative-eventing -n knative-eventing --timeout=$(TIMEOUT_SECS)

.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
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ 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/target/classes/workflow.sw.json /deployments/app/workflow.sw.json
EXPOSE 8080
USER 185
Expand Down
1 change: 0 additions & 1 deletion config/manager/SonataFlow-Builder.containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ 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/target/classes/workflow.sw.json /deployments/app/workflow.sw.json

EXPOSE 8080
USER 185
Expand Down
1 change: 0 additions & 1 deletion container-builder/builder/kubernetes/testdata/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ COPY --from=builder --chown=185 /home/kogito/kogito-sw-base/target/quarkus-app/l
COPY --from=builder --chown=185 /home/kogito/kogito-sw-base/target/quarkus-app/*.jar /deployments/
COPY --from=builder --chown=185 /home/kogito/kogito-sw-base/target/quarkus-app/app/ /deployments/app/
COPY --from=builder --chown=185 /home/kogito/kogito-sw-base/target/quarkus-app/quarkus/ /deployments/quarkus/
COPY --from=builder --chown=185 /home/kogito/kogito-sw-base/target/classes/workflow.sw.json /deployments/app/workflow.sw.json

EXPOSE 8080
USER 185
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ 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/target/classes/workflow.sw.json /deployments/app/workflow.sw.json

EXPOSE 8080
USER 185
Expand Down
2 changes: 1 addition & 1 deletion hack/ci/create-kind-cluster-with-registry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ ${container_engine} run \
-d --restart=always -p "127.0.0.1:${reg_port}:5000" --network bridge --name "${reg_name}" \
-v /tmp:/certs \
registry:2

# 4. Connect the registry to the cluster network if not already connected
# This allows kind to bootstrap the network but ensures they're on the same network
if [ "$("${container_engine}" inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
Expand Down
66 changes: 54 additions & 12 deletions internal/controller/validation/ImageValidator.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ package validation
import (
"archive/tar"
"context"
"crypto/tls"
"fmt"
"io"
"net/http"

operatorapi "github.com/apache/incubator-kie-kogito-serverless-operator/api/v1alpha08"
"github.com/google/go-cmp/cmp"
Expand All @@ -35,26 +37,29 @@ type ImageValidator struct{}
var workflowName = "deployments/app/workflow.sw.json"

func (v ImageValidator) Validate(ctx context.Context, client client.Client, sonataflow *operatorapi.SonataFlow, req ctrl.Request) error {
if sonataflow.HasContainerSpecImage() {
err := client.Get(ctx, req.NamespacedName, sonataflow)
equals, err := validateImage(sonataflow, sonataflow.Spec.PodTemplate.Container.Image)
if err != nil {
return err
}
if !equals {
return fmt.Errorf("Workflow, defined in the image %s doesn't match deployment workflow", sonataflow.Spec.PodTemplate.Container.Image)
}
equals, err := validateImage(ctx, sonataflow)
if err != nil {
return err
}
if !equals {
return fmt.Errorf("Workflow, defined in the image %s doesn't match deployment workflow", sonataflow.Spec.PodTemplate.Container.Image)
}
return nil
}

func validateImage(sonataflow *operatorapi.SonataFlow, image string) (bool, error) {
imageRef, err := name.ParseReference(image)
func validateImage(ctx context.Context, sonataflow *operatorapi.SonataFlow) (bool, error) {
isInKindRegistry, _, err := ImageStoredInKindRegistry(ctx, sonataflow.Spec.PodTemplate.Container.Image)
if err != nil {
return false, err
}

ref, err := remote.Image(imageRef)
var ref v1.Image
if isInKindRegistry {
ref, err = kindRegistryImage(sonataflow)
} else {
ref, err = remoteImage(sonataflow)
}

if err != nil {
return false, err
}
Expand All @@ -72,6 +77,43 @@ func validateImage(sonataflow *operatorapi.SonataFlow, image string) (bool, erro
return cmp.Equal(workflowDockerImage, sonataflow.Spec.Flow), nil
}

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
}

ref, err := remote.Image(imageRef)
if err != nil {
return nil, err
}
return ref, nil
}

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

transportOptions := []remote.Option{
remote.WithTransport(&http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}),
}

imageRef, err := name.ParseReference(sonataflow.Spec.PodTemplate.Container.Image, name.Insecure)
if err != nil {
return nil, err
}

ref, err := remote.Image(imageRef, transportOptions...)
if err != nil {
return nil, err
}
return ref, nil
}

func readLayers(image v1.Image, workflow string) (*tar.Reader, error) {
layers, err := image.Layers()
if err != nil {
Expand Down
10 changes: 6 additions & 4 deletions internal/controller/validation/Validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)

var validators = []Validator{ImageValidator{}}
var validators = []Validator{ImageValidator{}, ImageUrlSanitizer{}}

type Validator interface {
Validate(ctx context.Context, client client.Client, sonataflow *operatorapi.SonataFlow, req ctrl.Request) error
Expand All @@ -31,9 +31,11 @@ type Validator interface {
// validate the SonataFlow object, right now it's only check if workflow is in deployment has image declared as that image
// is the same as the image in the SonataFlow object
func Validate(ctx context.Context, client client.Client, sonataflow *operatorapi.SonataFlow, req ctrl.Request) error {
for _, validator := range validators {
if err := validator.Validate(ctx, client, sonataflow, req); err != nil {
return err
if sonataflow.HasContainerSpecImage() {
for _, validator := range validators {
if err := validator.Validate(ctx, client, sonataflow, req); err != nil {
return err
}
}
}
return nil
Expand Down
112 changes: 112 additions & 0 deletions internal/controller/validation/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// 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"
"net/url"
"strings"

"github.com/apache/incubator-kie-kogito-serverless-operator/internal/controller/platform"
"github.com/apache/incubator-kie-kogito-serverless-operator/utils"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func KindRegistryName(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 {
if data, ok := config.Data["localRegistryHosting.v1"]; ok {
result := platform.LocalRegistryHostingV1{}
if err := yaml.Unmarshal([]byte(data), &result); err == nil {
return result.HostFromClusterNetwork, nil
}
}
}
return "", nil
}

func checkUrlHasPrefix(input string) bool {
prefixes := []string{"http://", "https://", "docker://"}
for _, prefix := range prefixes {
if strings.HasPrefix(input, prefix) {
return true
}
}
return false
}

func hostAndPortFromUri(input string) (string, string, error) {
// we need to check if the input has a prefix, if not we add http://
// because the url.Parse function requires a scheme
if !checkUrlHasPrefix(input) {
input = "http://" + input
}

u, err := url.Parse(input)
if err != nil {
fmt.Println("Error parsing URL:", err)
}

host := u.Hostname()
port := u.Port()

// check if host is ip address
if net.ParseIP(host) == nil {
hosts, err := net.LookupIP(host)
if err != nil {
return "", "", fmt.Errorf("Failed to resolve domain: %v\n", err)
}
ipv4, err := getipv4(hosts)
if err != nil {
return "", "", fmt.Errorf("Failed to get ipv4 address: %v\n", err)
}
host = ipv4
}
return host, port, nil
}

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)
}
kindRegistryHost, kindRegistryPort, err := hostAndPortFromUri(kindRegistryHostAndPort)
if err != nil {
return false, "", fmt.Errorf("Failed to get kind registry host and port: %v\n", err)
}

imageHost, imagePort, err := hostAndPortFromUri(image)
if err != nil {
return false, "", fmt.Errorf("Failed to get image host and port: %v\n", err)
}
if imageHost == kindRegistryHost && imagePort == kindRegistryPort {
return true, kindRegistryHostAndPort, nil
}
return false, "", nil
}

func getipv4(ips []net.IP) (string, error) {
for _, ip := range ips {
if ip.To4() != nil {
return ip.String(), nil
}
}
return "", fmt.Errorf("No ipv4 address found")
}
65 changes: 65 additions & 0 deletions internal/controller/validation/imageurlsanitizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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
}
1 change: 0 additions & 1 deletion operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28206,7 +28206,6 @@ 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/target/classes/workflow.sw.json /deployments/app/workflow.sw.json

EXPOSE 8080
USER 185
Expand Down
1 change: 0 additions & 1 deletion test/builder/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ COPY --from=builder --chown=185 /home/kogito/kogito-base/target/quarkus-app/lib/
COPY --from=builder --chown=185 /home/kogito/kogito-base/target/quarkus-app/*.jar /deployments/
COPY --from=builder --chown=185 /home/kogito/kogito-base/target/quarkus-app/app/ /deployments/app/
COPY --from=builder --chown=185 /home/kogito/kogito-base/target/quarkus-app/quarkus/ /deployments/quarkus/
COPY --from=builder --chown=185 /home/kogito/kogito-base/target/classes/workflow.sw.json /deployments/app/workflow.sw.json

EXPOSE 8080
USER 185
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: quay.io/kiegroup/sonataflow-minimal-example:latest
image: kind-registry:5000/testimage/sonataflow-minimal-example:0.1
flow:
start: HelloWorld
states:
Expand Down
Loading

0 comments on commit b838cd2

Please sign in to comment.