diff --git a/.github/workflows/radix-operator-pr.yml b/.github/workflows/radix-operator-pr.yml index e2a899326..0f0acdfeb 100644 --- a/.github/workflows/radix-operator-pr.yml +++ b/.github/workflows/radix-operator-pr.yml @@ -4,6 +4,45 @@ on: branches: - master jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '1.21' + - name: Install dependencies + run: go mod download + - name: Run Tests + run: CGO_ENABLED=0 GOOS=linux go test `go list ./... | grep -v "pkg/client"` -timeout 2m + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + - uses: actions/setup-go@v4 + with: + go-version: '1.21' + - name: Install dependencies + run: go mod download + - name: Install GolangCI Lint + run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 + - name: Install StaticCheck + run: go install honnef.co/go/tools/cmd/staticcheck@2023.1.6 + + - name: golangci-lint + run: golangci-lint run --timeout=30m --max-same-issues=0 --out-format=github-actions --new + + # Add legacy checks since golangci-lint only tests changed files for now + - name: StaticCheck + run: staticcheck -f text `go list ./... | grep -v "pkg/client"` + - name: Go Vet + run: go vet `go list ./... | grep -v "pkg/client"` + build-operator: name: Build-operator runs-on: ubuntu-latest @@ -14,6 +53,7 @@ jobs: REF: ${{ github. sha }} DOCKER_BUILDKIT: 1 run: docker build -t radix-operator:${REF##*/} -f operator.Dockerfile . + build-pipeline: name: Build-pipeline runs-on: ubuntu-latest @@ -23,4 +63,4 @@ jobs: env: REF: ${{ github. sha }} DOCKER_BUILDKIT: 1 - run: docker build -t radix-operator:${REF##*/} -f pipeline.Dockerfile . \ No newline at end of file + run: docker build -t radix-operator:${REF##*/} -f pipeline.Dockerfile . diff --git a/.vscode/launch.json b/.vscode/launch.json index 1ce4d21b3..125264373 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -40,14 +40,15 @@ "program": "${workspaceFolder}/pipeline-runner/main.go", "env": {}, "args": [ - "--RADIX_APP=radix-job-demo", + "--RADIX_APP=oauth-demo", "--IMAGE_TAG=abcdef", - "--JOB_NAME=radix-pipeline-promotion-1", + "--JOB_NAME=radix-pipeline-20231121120818-sb2xq", "--PIPELINE_TYPE=promote", "--RADIX_TEKTON_IMAGE=radix-tekton:main-latest", - "--FROM_ENVIRONMENT=qa", + "--FROM_ENVIRONMENT=dev", "--TO_ENVIRONMENT=prod", - "--DEPLOYMENT_NAME=qa-etxkt-ac6rxchq", + "--DEPLOYMENT_NAME=dev-hyxzv-j9pg34k2", + "--RADIX_FILE_NAME=/workspace/radixconfig.yaml", "--DEBUG=true", "--RADIX_CONTAINER_REGISTRY=radixdev.azurecr.io" ] @@ -97,7 +98,7 @@ "RADIXOPERATOR_APP_ROLLING_UPDATE_MAX_SURGE": "25%", "RADIXOPERATOR_APP_READINESS_PROBE_INITIAL_DELAY_SECONDS": "5", "RADIXOPERATOR_APP_READINESS_PROBE_PERIOD_SECONDS": "10", - "RADIX_ACTIVE_CLUSTERNAME": "weekly-45", + "RADIX_ACTIVE_CLUSTERNAME": "weekly-47", "RADIX_IMAGE_BUILDER": "radix-image-builder:master-latest", "RADIX_TEKTON_IMAGE": "radix-tekton:main-latest", "RADIXOPERATOR_JOB_SCHEDULER": "radix-job-scheduler:main-latest", diff --git a/charts/radix-operator/templates/radix-operator-rbac.yaml b/charts/radix-operator/templates/radix-operator-rbac.yaml index c4efedb89..ce12ce4da 100644 --- a/charts/radix-operator/templates/radix-operator-rbac.yaml +++ b/charts/radix-operator/templates/radix-operator-rbac.yaml @@ -44,7 +44,7 @@ rules: verbs: - get - list - - watch + - watch - create - delete - update # required since radix operator grants this to others diff --git a/charts/radix-operator/templates/seccomp-profile-cm.yaml b/charts/radix-operator/templates/seccomp-profile-cm.yaml index f4a26e551..bb42a5f76 100644 --- a/charts/radix-operator/templates/seccomp-profile-cm.yaml +++ b/charts/radix-operator/templates/seccomp-profile-cm.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ .Values.seccompProfile.configMapName }} - namespace: {{ .Release.Namespace | quote }} + namespace: kube-system annotations: description: "configmap with seccomp profile suitable for running buildah. It's a copy of the moby container runtime's default seccomp profile, but with the addition of two syscalls: clone3 and unshare. https://github.com/moby/moby/blob/b335e3d305be86bd28089a057d8be6a346445549/profiles/seccomp/default.json" data: @@ -837,4 +837,4 @@ data: } } ] - } \ No newline at end of file + } diff --git a/charts/radix-operator/templates/seccomp-profile-daemonset.yaml b/charts/radix-operator/templates/seccomp-profile-daemonset.yaml index a545d3877..f49922723 100644 --- a/charts/radix-operator/templates/seccomp-profile-daemonset.yaml +++ b/charts/radix-operator/templates/seccomp-profile-daemonset.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: DaemonSet metadata: name: {{ .Values.seccompProfile.daemonSetName }} - namespace: {{ .Release.Namespace | quote }} + namespace: kube-system spec: selector: matchLabels: diff --git a/operator.Dockerfile b/operator.Dockerfile index fcabfe1f6..2bfbccfb4 100644 --- a/operator.Dockerfile +++ b/operator.Dockerfile @@ -13,16 +13,6 @@ RUN go mod download COPY ./radix-operator ./radix-operator COPY ./pkg ./pkg -FROM base as run-staticcheck -RUN go install honnef.co/go/tools/cmd/staticcheck@2023.1.3 -RUN staticcheck `go list ./... | grep -v "pkg/client"` && touch /staticcheck.done - -FROM base as tester -# Run tests -RUN go vet `go list ./... | grep -v "pkg/client"` && \ - CGO_ENABLED=0 GOOS=linux go test `go list ./... | grep -v "pkg/client"` && \ - touch /tests.done - FROM base as builder # Build WORKDIR /go/src/github.com/equinor/radix-operator/radix-operator/ @@ -35,8 +25,6 @@ FROM scratch COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /etc/passwd /etc/passwd COPY --from=builder /go/src/github.com/equinor/radix-operator/radix-operator/rootfs/radix-operator /usr/local/bin/radix-operator -# This will make sure staticcheck and tests are run before the final stage is built -COPY --from=run-staticcheck /staticcheck.done /staticcheck.done -COPY --from=tester /tests.done /tests.done + USER radix-operator ENTRYPOINT ["/usr/local/bin/radix-operator"] diff --git a/pipeline-runner/internal/hash/encoding.go b/pipeline-runner/internal/hash/encoding.go index 2192540d4..12af67a7a 100644 --- a/pipeline-runner/internal/hash/encoding.go +++ b/pipeline-runner/internal/hash/encoding.go @@ -6,7 +6,7 @@ import ( "math" "reflect" - yamlk8s "sigs.k8s.io/yaml" + "sigs.k8s.io/yaml" ) type encoder func(v any) ([]byte, error) @@ -39,7 +39,7 @@ func stringEncoder(v any) ([]byte, error) { } func structEncoder(v any) ([]byte, error) { - b, err := yamlk8s.Marshal(v) + b, err := yaml.Marshal(v) return b, err } diff --git a/pipeline-runner/internal/test/utils.go b/pipeline-runner/internal/test/utils.go new file mode 100644 index 000000000..085456e62 --- /dev/null +++ b/pipeline-runner/internal/test/utils.go @@ -0,0 +1,81 @@ +package test + +import ( + "context" + + "github.com/equinor/radix-operator/pipeline-runner/internal/hash" + "github.com/equinor/radix-operator/pipeline-runner/model" + pipelineDefaults "github.com/equinor/radix-operator/pipeline-runner/model/defaults" + "github.com/equinor/radix-operator/pkg/apis/defaults" + radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" + "github.com/equinor/radix-operator/pkg/apis/utils" + "gopkg.in/yaml.v3" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + yamlk8s "sigs.k8s.io/yaml" +) + +func CreatePreparePipelineConfigMapResponse(kubeClient kubernetes.Interface, configMapName, appName string, ra *radixv1.RadixApplication, buildCtx *model.PrepareBuildContext) error { + raBytes, err := yamlk8s.Marshal(ra) + if err != nil { + return err + } + data := map[string]string{ + pipelineDefaults.PipelineConfigMapContent: string(raBytes), + } + + if buildCtx != nil { + buildCtxBytes, err := yaml.Marshal(buildCtx) + if err != nil { + return err + } + data[pipelineDefaults.PipelineConfigMapBuildContext] = string(buildCtxBytes) + } + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: configMapName}, + Data: data, + } + _, err = kubeClient.CoreV1().ConfigMaps(utils.GetAppNamespace(appName)).Create(context.Background(), cm, metav1.CreateOptions{}) + return err +} + +func CreateGitInfoConfigMapResponse(kubeClient kubernetes.Interface, configMapName, appName, gitHash, gitTags string) error { + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: configMapName}, + Data: map[string]string{ + defaults.RadixGitCommitHashKey: gitHash, + defaults.RadixGitTagsKey: gitTags, + }, + } + _, err := kubeClient.CoreV1().ConfigMaps(utils.GetAppNamespace(appName)).Create(context.Background(), cm, metav1.CreateOptions{}) + return err +} + +func CreateBuildSecret(kubeClient kubernetes.Interface, appName string, data map[string][]byte) error { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: defaults.BuildSecretsName}, + Data: data, + } + + _, err := kubeClient.CoreV1().Secrets(utils.GetAppNamespace(appName)).Create(context.Background(), secret, metav1.CreateOptions{}) + return err +} + +func GetRadixApplicationHash(ra *radixv1.RadixApplication) string { + if ra == nil { + hash, _ := hash.ToHashString(hash.SHA256, "0nXSg9l6EUepshGFmolpgV3elB0m8Mv7") + return hash + } + hash, _ := hash.ToHashString(hash.SHA256, ra.Spec) + return hash +} + +func GetBuildSecretHash(secret *corev1.Secret) string { + if secret == nil { + hash, _ := hash.ToHashString(hash.SHA256, "34Wd68DsJRUzrHp2f63o3U5hUD6zl8Tj") + return hash + } + hash, _ := hash.ToHashString(hash.SHA256, secret.Data) + return hash +} diff --git a/pipeline-runner/main.go b/pipeline-runner/main.go index 2e561ebab..89351807a 100755 --- a/pipeline-runner/main.go +++ b/pipeline-runner/main.go @@ -103,9 +103,9 @@ func setPipelineArgsFromArguments(cmd *cobra.Command, pipelineArgs *model.Pipeli cmd.Flags().StringVar(&pipelineArgs.RadixConfigFile, defaults.RadixConfigFileEnvironmentVariable, "", "Radix config file name. Example: /workspace/radixconfig.yaml") cmd.Flags().StringVar(&pipelineArgs.ImageTag, defaults.RadixImageTagEnvironmentVariable, "latest", "Docker image tag") cmd.Flags().StringVar(&pipelineArgs.LogLevel, defaults.LogLevel, "INFO", "Log level: ERROR, INFO (default), DEBUG") - cmd.Flags().StringVar(&pipelineArgs.Builder.ResourcesLimitsMemory, defaults.OperatorAppBuilderResourcesLimitsMemoryEnvironmentVariable, "500M", "Image builder resource limit memory") + cmd.Flags().StringVar(&pipelineArgs.Builder.ResourcesLimitsMemory, defaults.OperatorAppBuilderResourcesLimitsMemoryEnvironmentVariable, "2000M", "Image builder resource limit memory") cmd.Flags().StringVar(&pipelineArgs.Builder.ResourcesRequestsCPU, defaults.OperatorAppBuilderResourcesRequestsCPUEnvironmentVariable, "200m", "Image builder resource requests CPU") - cmd.Flags().StringVar(&pipelineArgs.Builder.ResourcesRequestsMemory, defaults.OperatorAppBuilderResourcesRequestsMemoryEnvironmentVariable, "2000M", "Image builder resource requests memory") + cmd.Flags().StringVar(&pipelineArgs.Builder.ResourcesRequestsMemory, defaults.OperatorAppBuilderResourcesRequestsMemoryEnvironmentVariable, "500M", "Image builder resource requests memory") var useCache string cmd.Flags().StringVar(&useCache, defaults.RadixUseCacheEnvironmentVariable, "0", "Use cache") var pushImage string diff --git a/pipeline-runner/pipelines/app.go b/pipeline-runner/pipelines/app.go index 6fc490ef6..6d33bf15a 100644 --- a/pipeline-runner/pipelines/app.go +++ b/pipeline-runner/pipelines/app.go @@ -13,12 +13,12 @@ import ( radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned" monitoring "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" k8sErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" secretsstorevclient "sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned" + "sigs.k8s.io/yaml" ) // PipelineRunner Instance variables diff --git a/pipeline-runner/steps/apply_radixconfig.go b/pipeline-runner/steps/apply_radixconfig.go index b577792e4..1d334e7fe 100644 --- a/pipeline-runner/steps/apply_radixconfig.go +++ b/pipeline-runner/steps/apply_radixconfig.go @@ -2,7 +2,7 @@ package steps import ( "context" - "errors" + stderrors "errors" "fmt" "path/filepath" "strings" @@ -21,12 +21,12 @@ import ( operatorutils "github.com/equinor/radix-operator/pkg/apis/utils" "github.com/equinor/radix-operator/pkg/apis/utils/git" radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" kubeerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - yamlk8s "sigs.k8s.io/yaml" + "sigs.k8s.io/yaml" ) const ( @@ -90,11 +90,6 @@ func (cli *ApplyConfigStepImplementation) Run(pipelineInfo *model.PipelineInfo) applicationConfig := application.NewApplicationConfig(cli.GetKubeclient(), cli.GetKubeutil(), cli.GetRadixclient(), cli.GetRegistration(), ra) - err = applicationConfig.ApplyConfigToApplicationNamespace() - if err != nil { - return err - } - pipelineInfo.SetApplicationConfig(applicationConfig) if err := cli.setBuildSecret(pipelineInfo); err != nil { @@ -105,10 +100,6 @@ func (cli *ApplyConfigStepImplementation) Run(pipelineInfo *model.PipelineInfo) return err } - if pipelineInfo.IsPipelineType(radixv1.Deploy) && len(pipelineInfo.BuildComponentImages) > 0 { - return errors.New("deploy pipeline does not support building components and jobs") - } - if pipelineInfo.IsPipelineType(radixv1.BuildDeploy) { gitCommitHash, gitTags := cli.getHashAndTags(namespace, pipelineInfo) err = validate.GitTagsContainIllegalChars(gitTags) @@ -119,7 +110,11 @@ func (cli *ApplyConfigStepImplementation) Run(pipelineInfo *model.PipelineInfo) pipelineInfo.StopPipeline, pipelineInfo.StopPipelineMessage = getPipelineShouldBeStopped(pipelineInfo.PrepareBuildContext) } - return nil + if err := cli.validatePipelineInfo(pipelineInfo); err != nil { + return err + } + + return applicationConfig.ApplyConfigToApplicationNamespace() } func (cli *ApplyConfigStepImplementation) setBuildSecret(pipelineInfo *model.PipelineInfo) error { @@ -157,6 +152,14 @@ func (cli *ApplyConfigStepImplementation) setBuildAndDeployImages(pipelineInfo * return nil } +func (cli ApplyConfigStepImplementation) validatePipelineInfo(pipelineInfo *model.PipelineInfo) error { + if pipelineInfo.IsPipelineType(radixv1.Deploy) && len(pipelineInfo.BuildComponentImages) > 0 { + return ErrDeployOnlyPipelineDoesNotSupportBuild + } + + return validateDeployComponentImages(pipelineInfo.DeployEnvironmentComponentImages, pipelineInfo.RadixApplication) +} + func printEnvironmentComponentImageSources(imageSources environmentComponentSourceMap) { log.Info("Component image source in environments:") for envName, envInfo := range imageSources { @@ -521,7 +524,7 @@ func CreateRadixApplication(radixClient radixclient.Interface, // Important: Must use sigs.k8s.io/yaml decoder to correctly unmarshal Kubernetes objects. // This package supports encoding and decoding of yaml for CRD struct types using the json tag. // The gopkg.in/yaml.v3 package requires the yaml tag. - if err := yamlk8s.Unmarshal([]byte(configFileContent), ra); err != nil { + if err := yaml.Unmarshal([]byte(configFileContent), ra); err != nil { return nil, err } @@ -552,3 +555,26 @@ func getValueFromConfigMap(key string, configMap *corev1.ConfigMap) (string, err } return value, nil } + +func validateDeployComponentImages(deployComponentImages pipeline.DeployEnvironmentComponentImages, ra *radixv1.RadixApplication) error { + var errs []error + + for envName, components := range deployComponentImages { + for componentName, imageInfo := range components { + if strings.HasSuffix(imageInfo.ImagePath, radixv1.DynamicTagNameInEnvironmentConfig) { + if len(imageInfo.ImageTagName) > 0 { + continue + } + + env := ra.GetCommonComponentByName(componentName).GetEnvironmentConfigByName(envName) + if !commonutils.IsNil(env) && len(env.GetImageTagName()) > 0 { + continue + } + + errs = append(errs, errors.WithMessagef(ErrMissingRequiredImageTagName, "component %s in environment %s", componentName, envName)) + } + } + } + + return stderrors.Join(errs...) +} diff --git a/pipeline-runner/steps/apply_radixconfig_test.go b/pipeline-runner/steps/apply_radixconfig_test.go new file mode 100644 index 000000000..f83d6c67e --- /dev/null +++ b/pipeline-runner/steps/apply_radixconfig_test.go @@ -0,0 +1,268 @@ +package steps_test + +import ( + "context" + "testing" + + "github.com/equinor/radix-common/utils/pointers" + internaltest "github.com/equinor/radix-operator/pipeline-runner/internal/test" + "github.com/equinor/radix-operator/pipeline-runner/model" + "github.com/equinor/radix-operator/pipeline-runner/steps" + "github.com/equinor/radix-operator/pkg/apis/kube" + radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" + "github.com/equinor/radix-operator/pkg/apis/utils" + radixfake "github.com/equinor/radix-operator/pkg/client/clientset/versioned/fake" + prometheusfake "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/fake" + "github.com/stretchr/testify/suite" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kubefake "k8s.io/client-go/kubernetes/fake" +) + +func Test_RunApplyConfigTestSuite(t *testing.T) { + suite.Run(t, new(applyConfigTestSuite)) +} + +type applyConfigTestSuite struct { + suite.Suite + kubeClient *kubefake.Clientset + radixClient *radixfake.Clientset + promClient *prometheusfake.Clientset + kubeUtil *kube.Kube +} + +func (s *applyConfigTestSuite) SetupTest() { + s.kubeClient = kubefake.NewSimpleClientset() + s.radixClient = radixfake.NewSimpleClientset() + s.promClient = prometheusfake.NewSimpleClientset() + s.kubeUtil, _ = kube.New(s.kubeClient, s.radixClient, nil) +} + +func (s *applyConfigTestSuite) Test_Deploy_BuildComponentInDeployPiplineShouldFail() { + appName := "anyapp" + prepareConfigMapName := "preparecm" + rr := utils.NewRegistrationBuilder().WithName(appName).BuildRR() + _, _ = s.radixClient.RadixV1().RadixRegistrations().Create(context.Background(), rr, metav1.CreateOptions{}) + ra := utils.NewRadixApplicationBuilder(). + WithAppName(appName). + WithEnvironment("dev", "anybranch"). + WithComponents( + utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName("buildcomp"), + utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName("deploycomp").WithImage("any:latest"), + ). + BuildRA() + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + + pipeline := model.PipelineInfo{ + PipelineArguments: model.PipelineArguments{ + PipelineType: string(radixv1.Deploy), + ToEnvironment: "dev", + }, + RadixConfigMapName: prepareConfigMapName, + } + + applyStep := steps.NewApplyConfigStep() + applyStep.Init(s.kubeClient, s.radixClient, s.kubeUtil, s.promClient, rr) + err := applyStep.Run(&pipeline) + s.ErrorIs(err, steps.ErrDeployOnlyPipelineDoesNotSupportBuild) +} + +func (s *applyConfigTestSuite) Test_Deploy_BuildJobInDeployPiplineShouldFail() { + appName := "anyapp" + prepareConfigMapName := "preparecm" + rr := utils.NewRegistrationBuilder().WithName(appName).BuildRR() + _, _ = s.radixClient.RadixV1().RadixRegistrations().Create(context.Background(), rr, metav1.CreateOptions{}) + ra := utils.NewRadixApplicationBuilder(). + WithAppName(appName). + WithJobComponents( + utils.NewApplicationJobComponentBuilder().WithSchedulerPort(pointers.Ptr[int32](9999)).WithName("buildjob"), + utils.NewApplicationJobComponentBuilder().WithSchedulerPort(pointers.Ptr[int32](9999)).WithName("deployjob").WithImage("any:latest"), + ). + WithEnvironment("dev", "anybranch"). + BuildRA() + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + + pipeline := model.PipelineInfo{ + PipelineArguments: model.PipelineArguments{ + PipelineType: string(radixv1.Deploy), + ToEnvironment: "dev", + }, + RadixConfigMapName: prepareConfigMapName, + } + + applyStep := steps.NewApplyConfigStep() + applyStep.Init(s.kubeClient, s.radixClient, s.kubeUtil, s.promClient, rr) + err := applyStep.Run(&pipeline) + s.ErrorIs(err, steps.ErrDeployOnlyPipelineDoesNotSupportBuild) +} + +func (s *applyConfigTestSuite) Test_Deploy_ComponentWithMissingImageTagNameShouldFail() { + appName := "anyapp" + prepareConfigMapName := "preparecm" + rr := utils.NewRegistrationBuilder().WithName(appName).BuildRR() + _, _ = s.radixClient.RadixV1().RadixRegistrations().Create(context.Background(), rr, metav1.CreateOptions{}) + ra := utils.NewRadixApplicationBuilder(). + WithAppName(appName). + WithEnvironment("dev", "anybranch"). + WithComponents( + utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName("deploycomp").WithImage("any:{imageTagName}"), + ). + BuildRA() + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + + pipeline := model.PipelineInfo{ + PipelineArguments: model.PipelineArguments{ + PipelineType: string(radixv1.Deploy), + ToEnvironment: "dev", + }, + RadixConfigMapName: prepareConfigMapName, + } + + applyStep := steps.NewApplyConfigStep() + applyStep.Init(s.kubeClient, s.radixClient, s.kubeUtil, s.promClient, rr) + err := applyStep.Run(&pipeline) + s.ErrorIs(err, steps.ErrMissingRequiredImageTagName) + s.ErrorContains(err, "deploycomp") + s.ErrorContains(err, "dev") +} + +func (s *applyConfigTestSuite) Test_Deploy_ComponentWithImageTagNameInRAShouldSucceed() { + appName := "anyapp" + prepareConfigMapName := "preparecm" + rr := utils.NewRegistrationBuilder().WithName(appName).BuildRR() + _, _ = s.radixClient.RadixV1().RadixRegistrations().Create(context.Background(), rr, metav1.CreateOptions{}) + ra := utils.NewRadixApplicationBuilder(). + WithAppName(appName). + WithEnvironment("dev", "anybranch"). + WithComponents( + utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName("deploycomp").WithImage("any:{imageTagName}"). + WithEnvironmentConfig(utils.NewComponentEnvironmentBuilder().WithEnvironment("dev").WithImageTagName("anytag")), + ). + BuildRA() + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + + pipeline := model.PipelineInfo{ + PipelineArguments: model.PipelineArguments{ + PipelineType: string(radixv1.Deploy), + ToEnvironment: "dev", + }, + RadixConfigMapName: prepareConfigMapName, + } + + applyStep := steps.NewApplyConfigStep() + applyStep.Init(s.kubeClient, s.radixClient, s.kubeUtil, s.promClient, rr) + s.NoError(applyStep.Run(&pipeline)) +} + +func (s *applyConfigTestSuite) Test_Deploy_ComponentWithImageTagNameInPipelineArgShouldSucceed() { + appName := "anyapp" + prepareConfigMapName := "preparecm" + rr := utils.NewRegistrationBuilder().WithName(appName).BuildRR() + _, _ = s.radixClient.RadixV1().RadixRegistrations().Create(context.Background(), rr, metav1.CreateOptions{}) + ra := utils.NewRadixApplicationBuilder(). + WithAppName(appName). + WithEnvironment("dev", "anybranch"). + WithComponents( + utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName("deploycomp").WithImage("any:{imageTagName}"), + ). + BuildRA() + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + + pipeline := model.PipelineInfo{ + PipelineArguments: model.PipelineArguments{ + PipelineType: string(radixv1.Deploy), + ToEnvironment: "dev", + ImageTagNames: map[string]string{"deploycomp": "tag"}, + }, + RadixConfigMapName: prepareConfigMapName, + } + + applyStep := steps.NewApplyConfigStep() + applyStep.Init(s.kubeClient, s.radixClient, s.kubeUtil, s.promClient, rr) + s.NoError(applyStep.Run(&pipeline)) +} + +func (s *applyConfigTestSuite) Test_Deploy_JobWithMissingImageTagNameShouldFail() { + appName := "anyapp" + prepareConfigMapName := "preparecm" + rr := utils.NewRegistrationBuilder().WithName(appName).BuildRR() + _, _ = s.radixClient.RadixV1().RadixRegistrations().Create(context.Background(), rr, metav1.CreateOptions{}) + ra := utils.NewRadixApplicationBuilder(). + WithAppName(appName). + WithEnvironment("dev", "anybranch"). + WithJobComponents( + utils.NewApplicationJobComponentBuilder().WithSchedulerPort(pointers.Ptr[int32](9999)).WithName("deployjob").WithImage("any:{imageTagName}"), + ). + BuildRA() + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + + pipeline := model.PipelineInfo{ + PipelineArguments: model.PipelineArguments{ + PipelineType: string(radixv1.Deploy), + ToEnvironment: "dev", + }, + RadixConfigMapName: prepareConfigMapName, + } + + applyStep := steps.NewApplyConfigStep() + applyStep.Init(s.kubeClient, s.radixClient, s.kubeUtil, s.promClient, rr) + err := applyStep.Run(&pipeline) + s.ErrorIs(err, steps.ErrMissingRequiredImageTagName) + s.ErrorContains(err, "deployjob") + s.ErrorContains(err, "dev") +} + +func (s *applyConfigTestSuite) Test_Deploy_JobWithImageTagNameInRAShouldSucceed() { + appName := "anyapp" + prepareConfigMapName := "preparecm" + rr := utils.NewRegistrationBuilder().WithName(appName).BuildRR() + _, _ = s.radixClient.RadixV1().RadixRegistrations().Create(context.Background(), rr, metav1.CreateOptions{}) + ra := utils.NewRadixApplicationBuilder(). + WithAppName(appName). + WithEnvironment("dev", "anybranch"). + WithJobComponents( + utils.NewApplicationJobComponentBuilder().WithSchedulerPort(pointers.Ptr[int32](9999)).WithName("deployjob").WithImage("any:{imageTagName}"). + WithEnvironmentConfig(utils.NewJobComponentEnvironmentBuilder().WithEnvironment("dev").WithImageTagName("anytag")), + ). + BuildRA() + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + + pipeline := model.PipelineInfo{ + PipelineArguments: model.PipelineArguments{ + PipelineType: string(radixv1.Deploy), + ToEnvironment: "dev", + }, + RadixConfigMapName: prepareConfigMapName, + } + + applyStep := steps.NewApplyConfigStep() + applyStep.Init(s.kubeClient, s.radixClient, s.kubeUtil, s.promClient, rr) + s.NoError(applyStep.Run(&pipeline)) +} + +func (s *applyConfigTestSuite) Test_DeployComponentWitImageTagNameInPipelineArgShouldSucceed() { + appName := "anyapp" + prepareConfigMapName := "preparecm" + rr := utils.NewRegistrationBuilder().WithName(appName).BuildRR() + _, _ = s.radixClient.RadixV1().RadixRegistrations().Create(context.Background(), rr, metav1.CreateOptions{}) + ra := utils.NewRadixApplicationBuilder(). + WithAppName(appName). + WithEnvironment("dev", "anybranch"). + WithJobComponents( + utils.NewApplicationJobComponentBuilder().WithSchedulerPort(pointers.Ptr[int32](9999)).WithName("deployjob").WithImage("any:{imageTagName}"), + ). + BuildRA() + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + + pipeline := model.PipelineInfo{ + PipelineArguments: model.PipelineArguments{ + PipelineType: string(radixv1.Deploy), + ToEnvironment: "dev", + ImageTagNames: map[string]string{"deployjob": "anytag"}, + }, + RadixConfigMapName: prepareConfigMapName, + } + + applyStep := steps.NewApplyConfigStep() + applyStep.Init(s.kubeClient, s.radixClient, s.kubeUtil, s.promClient, rr) + s.NoError(applyStep.Run(&pipeline)) +} diff --git a/pipeline-runner/steps/build_test.go b/pipeline-runner/steps/build_test.go index 4e85821ab..32c949a6e 100644 --- a/pipeline-runner/steps/build_test.go +++ b/pipeline-runner/steps/build_test.go @@ -10,9 +10,9 @@ import ( "github.com/equinor/radix-common/utils/pointers" "github.com/equinor/radix-common/utils/slice" "github.com/equinor/radix-operator/pipeline-runner/internal/hash" + internaltest "github.com/equinor/radix-operator/pipeline-runner/internal/test" internalwait "github.com/equinor/radix-operator/pipeline-runner/internal/wait" "github.com/equinor/radix-operator/pipeline-runner/model" - pipelineDefaults "github.com/equinor/radix-operator/pipeline-runner/model/defaults" "github.com/equinor/radix-operator/pipeline-runner/steps" application "github.com/equinor/radix-operator/pkg/apis/applicationconfig" "github.com/equinor/radix-operator/pkg/apis/defaults" @@ -26,11 +26,9 @@ import ( "github.com/golang/mock/gomock" prometheusfake "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/fake" "github.com/stretchr/testify/suite" - "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kubefake "k8s.io/client-go/kubernetes/fake" - yamlk8s "sigs.k8s.io/yaml" ) func Test_RunBuildTestSuite(t *testing.T) { @@ -118,8 +116,8 @@ func (s *buildTestSuite) Test_BuildDeploy_JobSpecAndDeploymentConsistent() { WithEnvironment("prod", "release"). WithComponent(utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName(compName)). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) - s.Require().NoError(s.createGitInfoConfigMapResponse(gitConfigMapName, appName, gitHash, gitTags)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreateGitInfoConfigMapResponse(s.kubeClient, gitConfigMapName, appName, gitHash, gitTags)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ PipelineType: "build-deploy", @@ -219,7 +217,7 @@ func (s *buildTestSuite) Test_BuildDeploy_JobSpecAndDeploymentConsistent() { expectedRaHash, err := hash.ToHashString(hash.SHA256, ra.Spec) s.Require().NoError(err) s.Equal(expectedRaHash, rd.GetAnnotations()[kube.RadixConfigHash]) - s.Equal(s.getBuildSecretHash(nil), rd.GetAnnotations()[kube.RadixBuildSecretHash]) + s.Equal(internaltest.GetBuildSecretHash(nil), rd.GetAnnotations()[kube.RadixBuildSecretHash]) s.Greater(len(rd.GetAnnotations()[kube.RadixConfigHash]), 0) s.Require().Len(rd.Spec.Components, 1) s.Equal(compName, rd.Spec.Components[0].Name) @@ -258,7 +256,7 @@ func (s *buildTestSuite) Test_BuildJobSpec_MultipleComponents() { utils.NewApplicationJobComponentBuilder().WithSchedulerPort(jobPort).WithName("public-job-component").WithImage("job/job:latest"), ). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ PipelineType: "build-deploy", @@ -397,7 +395,7 @@ func (s *buildTestSuite) Test_BuildJobSpec_MultipleComponents_IgnoreDisabled() { WithEnvironmentConfig(utils.NewJobComponentEnvironmentBuilder().WithEnvironment(envName).WithEnabled(false)), ). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ PipelineType: "build-deploy", @@ -525,7 +523,7 @@ func (s *buildTestSuite) Test_BuildChangedComponents() { WithDeploymentName("currentrd"). WithAppName(appName). WithEnvironment(envName). - WithAnnotations(map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(ra), kube.RadixBuildSecretHash: s.getBuildSecretHash(nil)}). + WithAnnotations(map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(ra), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(nil)}). WithCondition(radixv1.DeploymentActive). WithComponents( utils.NewDeployComponentBuilder().WithName("comp-changed").WithImage("comp-changed-current:anytag"), @@ -547,7 +545,7 @@ func (s *buildTestSuite) Test_BuildChangedComponents() { {Environment: envName, Components: []string{"comp-changed", "comp-common1-changed", "comp-common3-changed", "job-changed", "job-common2-changed", "job-common3-changed"}}, }, } - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, buildCtx)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, buildCtx)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ PipelineType: "build-deploy", @@ -697,7 +695,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret hash unchanged, component changed, job changed - build all", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(currentBuildSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(currentBuildSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-changed-current:anytag")}, @@ -717,7 +715,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret hash unchanged, component changed - build component", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(currentBuildSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(currentBuildSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -737,7 +735,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret hash unchanged, job changed - build job", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(currentBuildSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(currentBuildSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -757,7 +755,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret hash unchanged, component unchanged, job unchanged - no build job", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(currentBuildSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(currentBuildSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -777,7 +775,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret hash unchanged, component unchanged, job unchanged, env namespace missing - build all", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(currentBuildSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(currentBuildSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -798,7 +796,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret hash unchanged, missing prepare context for environment - build all", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(currentBuildSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(currentBuildSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -818,7 +816,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret hash unchanged, component unchanged, job unchanged - no build job", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(currentBuildSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(currentBuildSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -838,7 +836,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash changed, buildsecret hash unchanged, component unchanged, job unchanged - build all", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(oldRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(currentBuildSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(oldRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(currentBuildSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -858,7 +856,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash missing, buildsecret hash unchanged, component unchanged, job unchanged - build all", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixBuildSecretHash: s.getBuildSecretHash(currentBuildSecret)}, + map[string]string{kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(currentBuildSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -878,7 +876,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret hash changed, component unchanged, job unchanged - build all", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(oldBuildSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(oldBuildSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -898,7 +896,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret magic hash, component unchanged, job unchanged - build all", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(nil)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(nil)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -918,7 +916,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret magic hash, no build secret, component unchanged, job unchanged - no build job", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(raWithoutSecret), kube.RadixBuildSecretHash: s.getBuildSecretHash(nil)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(raWithoutSecret), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(nil)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -939,7 +937,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret missing, no build secret, component unchanged, job unchanged - build all", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(raWithoutSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(raWithoutSecret)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -960,7 +958,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "radixconfig hash unchanged, buildsecret hash missing, component unchanged, job unchanged - build all", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa)}, radixv1.DeploymentActive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -994,7 +992,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { { name: "no current RD, component unchanged, job unchanged - build all", existingRd: radixDeploymentFactory( - map[string]string{kube.RadixConfigHash: s.getRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: s.getBuildSecretHash(currentBuildSecret)}, + map[string]string{kube.RadixConfigHash: internaltest.GetRadixApplicationHash(defaultRa), kube.RadixBuildSecretHash: internaltest.GetBuildSecretHash(currentBuildSecret)}, radixv1.DeploymentInactive, []utils.DeployComponentBuilder{utils.NewDeployComponentBuilder().WithName("comp").WithImage("comp-current:anytag")}, []utils.DeployJobComponentBuilder{utils.NewDeployJobComponentBuilder().WithName("job").WithImage("job-current:anytag")}, @@ -1029,7 +1027,7 @@ func (s *buildTestSuite) Test_DetectComponentsToBuild() { if !test.skipEnvNamespace { _, _ = s.kubeClient.CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: utils.GetEnvironmentNamespace(appName, envName)}}, metav1.CreateOptions{}) } - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, test.prepareBuildCtx)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, test.prepareBuildCtx)) pipeline := model.PipelineInfo{PipelineArguments: piplineArgs, RadixConfigMapName: prepareConfigMapName} applyStep := steps.NewApplyConfigStep() applyStep.Init(s.kubeClient, s.radixClient, s.kubeUtil, s.promClient, rr) @@ -1096,7 +1094,7 @@ func (s *buildTestSuite) Test_BuildJobSpec_ImageTagNames() { WithEnvironmentConfig(utils.NewJobComponentEnvironmentBuilder().WithEnvironment(envName).WithImageTagName("job2envtag")), ). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ PipelineType: "deploy", @@ -1153,7 +1151,7 @@ func (s *buildTestSuite) Test_BuildJobSpec_PushImage() { WithEnvironment("dev", "main"). WithComponent(utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName(compName)). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ Branch: "main", @@ -1193,7 +1191,7 @@ func (s *buildTestSuite) Test_BuildJobSpec_UseCache() { WithEnvironment("dev", "main"). WithComponent(utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName(compName)). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ Branch: "main", @@ -1233,7 +1231,7 @@ func (s *buildTestSuite) Test_BuildJobSpec_WithDockerfileName() { WithEnvironment("dev", "main"). WithComponent(utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName(compName).WithDockerfileName(dockerFileName)). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ Branch: "main", @@ -1272,7 +1270,7 @@ func (s *buildTestSuite) Test_BuildJobSpec_WithSourceFolder() { WithEnvironment("dev", "main"). WithComponent(utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName(compName).WithSourceFolder(".././path/../../subpath")). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ Branch: "main", @@ -1312,8 +1310,8 @@ func (s *buildTestSuite) Test_BuildJobSpec_WithBuildSecrets() { WithEnvironment(envName, "main"). WithComponent(utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName(compName)). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) - s.Require().NoError(s.createBuildSecret(appName, map[string][]byte{"SECRET1": nil, "SECRET2": nil})) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreateBuildSecret(s.kubeClient, appName, map[string][]byte{"SECRET1": nil, "SECRET2": nil})) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ Branch: "main", @@ -1367,7 +1365,7 @@ func (s *buildTestSuite) Test_BuildJobSpec_BuildKit() { WithEnvironment("dev", "main"). WithComponent(utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName(compName).WithDockerfileName(dockerFile).WithSourceFolder(sourceFolder)). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ Branch: "main", @@ -1430,7 +1428,7 @@ func (s *buildTestSuite) Test_BuildJobSpec_BuildKit_PushImage() { WithEnvironment("dev", "main"). WithComponent(utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName(compName).WithDockerfileName(dockerFile).WithSourceFolder(sourceFolder)). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ Branch: "main", @@ -1501,8 +1499,8 @@ func (s *buildTestSuite) Test_BuildJobSpec_BuildKit_WithBuildSecrets() { WithEnvironment("dev", "main"). WithComponent(utils.NewApplicationComponentBuilder().WithPort("any", 8080).WithName(compName).WithDockerfileName(dockerFile).WithSourceFolder(sourceFolder)). BuildRA() - s.Require().NoError(s.createPreparePipelineConfigMapResponse(prepareConfigMapName, appName, ra, nil)) - s.Require().NoError(s.createBuildSecret(appName, map[string][]byte{"SECRET1": nil, "SECRET2": nil})) + s.Require().NoError(internaltest.CreatePreparePipelineConfigMapResponse(s.kubeClient, prepareConfigMapName, appName, ra, nil)) + s.Require().NoError(internaltest.CreateBuildSecret(s.kubeClient, appName, map[string][]byte{"SECRET1": nil, "SECRET2": nil})) pipeline := model.PipelineInfo{ PipelineArguments: model.PipelineArguments{ Branch: "main", @@ -1561,67 +1559,3 @@ func (s *buildTestSuite) Test_BuildJobSpec_BuildKit_WithBuildSecrets() { expectedCommand := []string{"/bin/bash", "-c", expectedBuildCmd} s.Equal(expectedCommand, job.Spec.Template.Spec.Containers[0].Command) } - -func (s *buildTestSuite) createPreparePipelineConfigMapResponse(configMapName, appName string, ra *radixv1.RadixApplication, buildCtx *model.PrepareBuildContext) error { - raBytes, err := yamlk8s.Marshal(ra) - if err != nil { - return err - } - data := map[string]string{ - pipelineDefaults.PipelineConfigMapContent: string(raBytes), - } - - if buildCtx != nil { - buildCtxBytes, err := yaml.Marshal(buildCtx) - if err != nil { - return err - } - data[pipelineDefaults.PipelineConfigMapBuildContext] = string(buildCtxBytes) - } - cm := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: configMapName}, - Data: data, - } - _, err = s.kubeClient.CoreV1().ConfigMaps(utils.GetAppNamespace(appName)).Create(context.Background(), cm, metav1.CreateOptions{}) - return err -} - -func (s *buildTestSuite) createGitInfoConfigMapResponse(configMapName, appName, gitHash, gitTags string) error { - cm := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: configMapName}, - Data: map[string]string{ - defaults.RadixGitCommitHashKey: gitHash, - defaults.RadixGitTagsKey: gitTags, - }, - } - _, err := s.kubeClient.CoreV1().ConfigMaps(utils.GetAppNamespace(appName)).Create(context.Background(), cm, metav1.CreateOptions{}) - return err -} - -func (s *buildTestSuite) createBuildSecret(appName string, data map[string][]byte) error { - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: defaults.BuildSecretsName}, - Data: data, - } - - _, err := s.kubeClient.CoreV1().Secrets(utils.GetAppNamespace(appName)).Create(context.Background(), secret, metav1.CreateOptions{}) - return err -} - -func (s *buildTestSuite) getRadixApplicationHash(ra *radixv1.RadixApplication) string { - if ra == nil { - hash, _ := hash.ToHashString(hash.SHA256, "0nXSg9l6EUepshGFmolpgV3elB0m8Mv7") - return hash - } - hash, _ := hash.ToHashString(hash.SHA256, ra.Spec) - return hash -} - -func (s *buildTestSuite) getBuildSecretHash(secret *corev1.Secret) string { - if secret == nil { - hash, _ := hash.ToHashString(hash.SHA256, "34Wd68DsJRUzrHp2f63o3U5hUD6zl8Tj") - return hash - } - hash, _ := hash.ToHashString(hash.SHA256, secret.Data) - return hash -} diff --git a/pipeline-runner/steps/errors.go b/pipeline-runner/steps/errors.go new file mode 100644 index 000000000..623f53d1a --- /dev/null +++ b/pipeline-runner/steps/errors.go @@ -0,0 +1,10 @@ +package steps + +import ( + "github.com/pkg/errors" +) + +var ( + ErrDeployOnlyPipelineDoesNotSupportBuild = errors.New("deploy pipeline does not support building components and jobs") + ErrMissingRequiredImageTagName = errors.New("missing required imageTagName in environmentConfig or pipeline argument") +) diff --git a/pipeline.Dockerfile b/pipeline.Dockerfile index 1e6e7161e..37ac19130 100644 --- a/pipeline.Dockerfile +++ b/pipeline.Dockerfile @@ -15,15 +15,6 @@ RUN go mod download COPY ./pipeline-runner ./pipeline-runner COPY ./pkg ./pkg -FROM base as run-staticcheck -RUN staticcheck `go list ./... | grep -v "pkg/client"` && touch /staticcheck.done - -FROM base as tester -# Run tests -RUN go vet `go list ./... | grep -v "pkg/client"` && \ - CGO_ENABLED=0 GOOS=linux go test `go list ./... | grep -v "pkg/client"` && \ - touch /tests.done - # Build FROM base as builder WORKDIR /go/src/github.com/equinor/radix-operator/pipeline-runner/ @@ -35,8 +26,6 @@ FROM scratch COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /etc/passwd /etc/passwd COPY --from=builder /go/src/github.com/equinor/radix-operator/pipeline-runner/rootfs/pipeline-runner /usr/local/bin/pipeline-runner -# This will make sure staticcheck and tests are run before the final stage is built -COPY --from=run-staticcheck /staticcheck.done /staticcheck.done -COPY --from=tester /tests.done /tests.done + USER radix-pipeline ENTRYPOINT ["/usr/local/bin/pipeline-runner"] diff --git a/pkg/apis/deployment/ingress.go b/pkg/apis/deployment/ingress.go index 27a6f8aa3..af8f8749a 100644 --- a/pkg/apis/deployment/ingress.go +++ b/pkg/apis/deployment/ingress.go @@ -18,7 +18,7 @@ import ( // IngressConfiguration Holds all ingress annotation configurations type IngressConfiguration struct { - AnnotationConfigurations []AnnotationConfiguration `yaml:"configuration"` + AnnotationConfigurations []AnnotationConfiguration `json:"configuration" yaml:"configuration"` } // AnnotationConfiguration Holds annotations for a single configuration diff --git a/pkg/apis/deployment/radixcommoncomponent.go b/pkg/apis/deployment/radixcommoncomponent.go index dbe2d88bc..37060eafc 100644 --- a/pkg/apis/deployment/radixcommoncomponent.go +++ b/pkg/apis/deployment/radixcommoncomponent.go @@ -91,16 +91,9 @@ func getImagePath(componentName string, componentImage pipeline.DeployComponentI // For deploy-only images, we will replace the dynamic tag with the tag from the environment config return strings.ReplaceAll(image, v1.DynamicTagNameInEnvironmentConfig, imageTagName), nil } - if len(imageTagName) > 0 { - return "", errorNotExpectedImageTagNameInImage(componentName, imageTagName) - } return image, nil } -func errorNotExpectedImageTagNameInImage(componentImageName, imageTagName string) error { - return fmt.Errorf("image property for a component %s does not have a dynamic imageTagName but it is provided: %s", componentImageName, imageTagName) -} - func errorMissingExpectedDynamicImageTagName(componentName string) error { return fmt.Errorf(fmt.Sprintf("component %s is missing an expected dynamic imageTagName for its image", componentName)) } diff --git a/pkg/apis/deployment/radixcomponent_test.go b/pkg/apis/deployment/radixcomponent_test.go index 4ea10d24d..349214acd 100644 --- a/pkg/apis/deployment/radixcomponent_test.go +++ b/pkg/apis/deployment/radixcomponent_test.go @@ -833,28 +833,6 @@ func TestGetRadixComponentsForEnv_ImageWithImageTagName(t *testing.T) { }, expectedError: errorMissingExpectedDynamicImageTagName(componentName1), }, - { - name: "static image name, but component env config image-tags provided", - componentImages: map[string]string{ - componentName1: staticImageName1, - componentName2: staticImageName2, - }, - environmentConfigImageTagNames: map[string]string{ - componentName2: "tag-component-a", - }, - expectedError: errorNotExpectedImageTagNameInImage(componentName2, "tag-component-a"), - }, - { - name: "static image name, but external image-tags provided", - componentImages: map[string]string{ - componentName1: staticImageName1, - componentName2: staticImageName2, - }, - externalImageTagNames: map[string]string{ - componentName1: "tag-component-a", - }, - expectedError: errorNotExpectedImageTagNameInImage(componentName1, "tag-component-a"), - }, { name: "with image-tags", componentImages: map[string]string{ diff --git a/pkg/apis/deployment/radixjobcomponent_test.go b/pkg/apis/deployment/radixjobcomponent_test.go index 2624fa544..1926ffa81 100644 --- a/pkg/apis/deployment/radixjobcomponent_test.go +++ b/pkg/apis/deployment/radixjobcomponent_test.go @@ -544,28 +544,6 @@ func TestGetRadixJobComponentsForEnv_ImageWithImageTagName(t *testing.T) { }, expectedError: errorMissingExpectedDynamicImageTagName(componentName1), }, - { - name: "static image name, but component env config image-tags provided", - componentImages: map[string]string{ - componentName1: staticImageName1, - componentName2: staticImageName2, - }, - environmentConfigImageTagNames: map[string]string{ - componentName2: "tag-component-a", - }, - expectedError: errorNotExpectedImageTagNameInImage(componentName2, "tag-component-a"), - }, - { - name: "static image name, but external image-tags provided", - componentImages: map[string]string{ - componentName1: staticImageName1, - componentName2: staticImageName2, - }, - externalImageTagNames: map[string]string{ - componentName1: "tag-component-a", - }, - expectedError: errorNotExpectedImageTagNameInImage(componentName1, "tag-component-a"), - }, { name: "with image-tags", componentImages: map[string]string{ diff --git a/pkg/apis/kube/secret_provider.go b/pkg/apis/kube/secret_provider.go index 130957c42..e4b55c2a0 100644 --- a/pkg/apis/kube/secret_provider.go +++ b/pkg/apis/kube/secret_provider.go @@ -9,10 +9,10 @@ import ( "github.com/equinor/radix-operator/pkg/apis/defaults" radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v3" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" secretsstorev1 "sigs.k8s.io/secrets-store-csi-driver/apis/v1" + "sigs.k8s.io/yaml" ) const ( @@ -31,17 +31,17 @@ type stringArray struct { // SecretProviderClassParameterObject Object for SecretProviderClass parameters type SecretProviderClassParameterObject struct { // Name. Name of the Azure Key Vault object - Name string `yaml:"objectName"` + Name string `json:"objectName" yaml:"objectName"` // Type. Type of the Azure KeyVault object: secret, key, cert - Type string `yaml:"objectType"` + Type string `json:"objectType" yaml:"objectType"` // Alias. Optional. Specify the filename of the object when written to disk. Defaults to objectName if not provided. - Alias string `yaml:"objectAlias,omitempty"` + Alias string `json:"objectAlias,omitempty" yaml:"objectAlias,omitempty"` // Version. Optional. object versions, default to the latest if empty - Version string `yaml:"objectVersion,omitempty"` + Version string `json:"objectVersion,omitempty" yaml:"objectVersion,omitempty"` // Format. Optional. The format of the Azure Key Vault object, supported types are pem and pfx. objectFormat: pfx is only supported with objectType: secret and PKCS12 or ECC certificates. Default format for certificates is pem. - Format string `yaml:"objectFormat,omitempty"` + Format string `json:"objectFormat,omitempty" yaml:"objectFormat,omitempty"` // Encoding. Optional. Setting object encoding to base64 and object format to pfx will fetch and write the base64 decoded pfx binary - Encoding string `yaml:"objectEncoding,omitempty"` + Encoding string `json:"objectEncoding,omitempty" yaml:"objectEncoding,omitempty"` } // GetSecretProviderClass Gets secret provider class diff --git a/pkg/apis/radixvalidators/errors.go b/pkg/apis/radixvalidators/errors.go index 5606c1189..f63ecffd2 100644 --- a/pkg/apis/radixvalidators/errors.go +++ b/pkg/apis/radixvalidators/errors.go @@ -45,8 +45,6 @@ var ( ErrunknownVolumeMountType = errors.New("unknown volume mount type") ErrApplicationNameNotLowercase = errors.New("application name not lowercase") ErrPublicImageComponentCannotHaveSourceOrDockerfileSet = errors.New("public image component cannot have source or dockerfile") - ErrComponentWithDynamicTagRequiresTagInEnvironmentConfig = errors.New("component with dynamic tag requires tag in environment") - ErrComponentWithDynamicTagRequiresTagInEnvironmentConfigForEnvironment = errors.New("component with dynamic tag requires tag in environment config for") ErrComponentWithTagInEnvironmentConfigForEnvironmentRequiresDynamicTag = errors.New("component with tag in environment config for environment requires dynamic") ErrComponentNameReservedSuffix = errors.New("component name reserved suffix") ErrSecretNameConflictsWithEnvironmentVariable = errors.New("secret name conflicts with environment") @@ -268,17 +266,6 @@ func PublicImageComponentCannotHaveSourceOrDockerfileSetWithMessage(componentNam return errors.WithMessagef(ErrPublicImageComponentCannotHaveSourceOrDockerfileSet, "component %s cannot have neither 'src' nor 'Dockerfile' set", componentName) } -// ComponentWithDynamicTagRequiresTagInEnvironmentConfigWithMessage Error if image is set with dynamic tag and tag is missing -func ComponentWithDynamicTagRequiresTagInEnvironmentConfigWithMessage(componentName string) error { - return errors.WithMessagef(ErrComponentWithDynamicTagRequiresTagInEnvironmentConfig, "component %s with %s on image requires an image tag set on environment config", - componentName, radixv1.DynamicTagNameInEnvironmentConfig) -} - -// ComponentWithDynamicTagRequiresTagInEnvironmentConfigForEnvironmentWithMessage Error if image is set with dynamic tag and tag is missing -func ComponentWithDynamicTagRequiresTagInEnvironmentConfigForEnvironmentWithMessage(componentName, environment string) error { - return errors.WithMessagef(ErrComponentWithDynamicTagRequiresTagInEnvironmentConfigForEnvironment, "component %s with %s on image requires an image tag set on environment config for environment %s", componentName, radixv1.DynamicTagNameInEnvironmentConfig, environment) -} - // ComponentWithTagInEnvironmentConfigForEnvironmentRequiresDynamicTagWithMessage If tag is set then the dynamic tag needs to be set on the image func ComponentWithTagInEnvironmentConfigForEnvironmentRequiresDynamicTagWithMessage(componentName, environment string) error { return errors.WithMessagef(ErrComponentWithTagInEnvironmentConfigForEnvironmentRequiresDynamicTag, "component %s with image tag set on environment config for environment %s requires %s on image setting", componentName, environment, radixv1.DynamicTagNameInEnvironmentConfig) diff --git a/pkg/apis/radixvalidators/testdata/radixconfig.yaml b/pkg/apis/radixvalidators/testdata/radixconfig.yaml index 013828190..35706f7ba 100644 --- a/pkg/apis/radixvalidators/testdata/radixconfig.yaml +++ b/pkg/apis/radixvalidators/testdata/radixconfig.yaml @@ -87,6 +87,20 @@ spec: ports: - name: http port: 8090 + - name: compimg1 + image: img:{imageTagName} + ports: + - name: http + port: 8090 + - name: compimg2 + image: img:{imageTagName} + ports: + - name: http + port: 8090 + environmentConfig: + - environment: dev + - environment: prod + imageTagName: "1.0.0" jobs: - name: job src: job/ @@ -138,6 +152,16 @@ spec: ports: - name: http port: 8099 + - name: jobimg1 + schedulerPort: 8000 + image: img:{imageTagName} + - name: jobimg2 + schedulerPort: 8000 + image: img:{imageTagName} + environmentConfig: + - environment: dev + - environment: prod + imageTagName: "1.0.0" dnsAppAlias: environment: prod component: app \ No newline at end of file diff --git a/pkg/apis/radixvalidators/validate_ra.go b/pkg/apis/radixvalidators/validate_ra.go index 82d8adc63..a16285b9d 100644 --- a/pkg/apis/radixvalidators/validate_ra.go +++ b/pkg/apis/radixvalidators/validate_ra.go @@ -212,19 +212,6 @@ func validateComponents(app *radixv1.RadixApplication) error { errs = append(errs, PublicImageComponentCannotHaveSourceOrDockerfileSetWithMessage(component.Name)) } - if usesDynamicTaggingForDeployOnly(component.Image) { - if len(component.EnvironmentConfig) == 0 { - errs = append(errs, ComponentWithDynamicTagRequiresTagInEnvironmentConfigWithMessage(component.Name)) - } else { - for _, environment := range component.EnvironmentConfig { - if doesEnvExistAndIsMappedToBranch(app, environment.Environment) && environment.ImageTagName == "" { - errs = append(errs, - ComponentWithDynamicTagRequiresTagInEnvironmentConfigForEnvironmentWithMessage(component.Name, environment.Environment)) - } - } - } - } - err := validateComponentName(component.Name, "component") if err != nil { errs = append(errs, err) @@ -296,19 +283,6 @@ func validateJobComponents(app *radixv1.RadixApplication) error { errs = append(errs, PublicImageComponentCannotHaveSourceOrDockerfileSetWithMessage(job.Name)) } - if usesDynamicTaggingForDeployOnly(job.Image) { - if len(job.EnvironmentConfig) == 0 { - errs = append(errs, ComponentWithDynamicTagRequiresTagInEnvironmentConfigWithMessage(job.Name)) - } else { - for _, environment := range job.EnvironmentConfig { - if doesEnvExistAndIsMappedToBranch(app, environment.Environment) && environment.ImageTagName == "" { - errs = append(errs, - ComponentWithDynamicTagRequiresTagInEnvironmentConfigForEnvironmentWithMessage(job.Name, environment.Environment)) - } - } - } - } - err := validateComponentName(job.Name, "job") if err != nil { errs = append(errs, err) @@ -538,11 +512,6 @@ func validateOAuth(oauth *radixv1.OAuth2, componentName, environmentName string) return } -func usesDynamicTaggingForDeployOnly(componentImage string) bool { - return componentImage != "" && - strings.HasSuffix(componentImage, radixv1.DynamicTagNameInEnvironmentConfig) -} - func environmentHasDynamicTaggingButImageLacksTag(environmentImageTag, componentImage string) bool { return environmentImageTag != "" && (componentImage == "" || @@ -1318,11 +1287,6 @@ func doesEnvExist(app *radixv1.RadixApplication, name string) bool { return getEnv(app, name) != nil } -func doesEnvExistAndIsMappedToBranch(app *radixv1.RadixApplication, name string) bool { - env := getEnv(app, name) - return env != nil && env.Build.From != "" -} - func getEnv(app *radixv1.RadixApplication, name string) *radixv1.Environment { for _, env := range app.Spec.Environments { if env.Name == name { diff --git a/pkg/apis/radixvalidators/validate_ra_test.go b/pkg/apis/radixvalidators/validate_ra_test.go index da735c179..1d9e12004 100644 --- a/pkg/apis/radixvalidators/validate_ra_test.go +++ b/pkg/apis/radixvalidators/validate_ra_test.go @@ -318,18 +318,6 @@ func Test_invalid_ra(t *testing.T) { ra.Spec.Components[0].SourceFolder = "./api" ra.Spec.Components[0].DockerfileName = ".Dockerfile" }}, - {"missing environment config for dynamic tag", radixvalidators.ComponentWithDynamicTagRequiresTagInEnvironmentConfigWithMessage(validRAFirstComponentName), func(ra *v1.RadixApplication) { - ra.Spec.Components[0].Image = "radixcanary.azurecr.io/my-private-image:{imageTagName}" - ra.Spec.Components[0].EnvironmentConfig = []v1.RadixEnvironmentConfig{} - }}, - {"missing dynamic tag config for mapped environment", radixvalidators.ComponentWithDynamicTagRequiresTagInEnvironmentConfigForEnvironmentWithMessage(validRAFirstComponentName, "dev"), func(ra *v1.RadixApplication) { - ra.Spec.Components[0].Image = "radixcanary.azurecr.io/my-private-image:{imageTagName}" - ra.Spec.Components[0].EnvironmentConfig[0].ImageTagName = "" - ra.Spec.Components[0].EnvironmentConfig = append(ra.Spec.Components[0].EnvironmentConfig, v1.RadixEnvironmentConfig{ - Environment: "dev", - ImageTagName: "", - }) - }}, {"inconcistent dynamic tag config for environment", radixvalidators.ComponentWithTagInEnvironmentConfigForEnvironmentRequiresDynamicTagWithMessage(validRAFirstComponentName, "prod"), func(ra *v1.RadixApplication) { ra.Spec.Components[0].Image = "radixcanary.azurecr.io/my-private-image:some-tag" ra.Spec.Components[0].EnvironmentConfig[0].ImageTagName = "any-tag" @@ -456,18 +444,6 @@ func Test_invalid_ra(t *testing.T) { ra.Spec.Jobs[0].SourceFolder = "./api" ra.Spec.Jobs[0].DockerfileName = ".Dockerfile" }}, - {"job missing environment config for dynamic tag", radixvalidators.ComponentWithDynamicTagRequiresTagInEnvironmentConfigWithMessage(validRAFirstJobName), func(ra *v1.RadixApplication) { - ra.Spec.Jobs[0].Image = "radixcanary.azurecr.io/my-private-image:{imageTagName}" - ra.Spec.Jobs[0].EnvironmentConfig = []v1.RadixJobComponentEnvironmentConfig{} - }}, - {"job missing dynamic tag config for mapped environment", radixvalidators.ComponentWithDynamicTagRequiresTagInEnvironmentConfigForEnvironmentWithMessage(validRAFirstJobName, "dev"), func(ra *v1.RadixApplication) { - ra.Spec.Jobs[0].Image = "radixcanary.azurecr.io/my-private-image:{imageTagName}" - ra.Spec.Jobs[0].EnvironmentConfig[0].ImageTagName = "" - ra.Spec.Jobs[0].EnvironmentConfig = append(ra.Spec.Jobs[0].EnvironmentConfig, v1.RadixJobComponentEnvironmentConfig{ - Environment: "dev", - ImageTagName: "", - }) - }}, {"job inconcistent dynamic tag config for environment", radixvalidators.ComponentWithTagInEnvironmentConfigForEnvironmentRequiresDynamicTagWithMessage(validRAFirstJobName, "dev"), func(ra *v1.RadixApplication) { ra.Spec.Jobs[0].Image = "radixcanary.azurecr.io/my-private-image:some-tag" ra.Spec.Jobs[0].EnvironmentConfig[0].ImageTagName = "any-tag"