diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9aa4decdb..7075a68451 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,6 +121,7 @@ jobs: # Build and load the Git and Bundle image export GIT_CONTAINER_IMAGE="$(KO_DOCKER_REPO=kind.local ko publish ./cmd/git)" export BUNDLE_CONTAINER_IMAGE="$(KO_DOCKER_REPO=kind.local ko publish ./cmd/bundle)" + export IMAGE_PROCESSING_CONTAINER_IMAGE="$(KO_DOCKER_REPO=kind.local ko publish ./cmd/image-processing)" make test-integration diff --git a/test/integration/vulnerability_scan_test.go b/test/integration/vulnerability_scan_test.go new file mode 100644 index 0000000000..6777dd4d6a --- /dev/null +++ b/test/integration/vulnerability_scan_test.go @@ -0,0 +1,159 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package integration_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + + buildapi "github.com/shipwright-io/build/pkg/apis/build/v1beta1" + test "github.com/shipwright-io/build/test/v1beta1_samples" +) + +var _ = Describe("Vulnerability scan tests", func() { + + var ( + cbsObject *buildapi.ClusterBuildStrategy + buildObject *buildapi.Build + buildRunObject *buildapi.BuildRun + buildSample []byte + buildRunSample []byte + ) + + // Load the ClusterBuildStrategies before each test case + BeforeEach(func() { + cbsObject, err = tb.Catalog.LoadCBSWithName(STRATEGY+tb.Namespace, []byte(test.ClusterBuildStrategyForVulnerabilityScanning)) + Expect(err).To(BeNil()) + + err = tb.CreateClusterBuildStrategy(cbsObject) + Expect(err).To(BeNil()) + + }) + + // Delete the ClusterBuildStrategies after each test case + AfterEach(func() { + if buildObject != nil { + _, err = tb.GetBuild(buildObject.Name) + if err == nil { + Expect(tb.DeleteBuild(buildObject.Name)).To(BeNil()) + } + } + + err := tb.DeleteClusterBuildStrategy(cbsObject.Name) + Expect(err).To(BeNil()) + }) + + // Override the Builds and BuildRuns CRDs instances to use + // before an It() statement is executed + JustBeforeEach(func() { + if buildSample != nil { + buildObject, err = tb.Catalog.LoadBuildWithNameAndStrategy(BUILD+tb.Namespace, STRATEGY+tb.Namespace, buildSample) + Expect(err).To(BeNil()) + } + + if buildRunSample != nil { + buildRunObject, err = tb.Catalog.LoadBRWithNameAndRef(BUILDRUN+tb.Namespace, BUILD+tb.Namespace, buildRunSample) + Expect(err).To(BeNil()) + } + }) + + Context("when a build with vulnerability scan options is defined", func() { + + BeforeEach(func() { + buildSample = []byte(test.BuildWithVulnerabilityScanOptions) + buildRunSample = []byte(test.MinimalBuildRun) + }) + + It("should fail the builRun with a Reason", func() { + + Expect(tb.CreateBuild(buildObject)).To(BeNil()) + + buildObject, err = tb.GetBuildTillValidation(buildObject.Name) + Expect(err).To(BeNil()) + + Expect(tb.CreateBR(buildRunObject)).To(BeNil()) + + br, err := tb.GetBRTillCompletion(buildRunObject.Name) + Expect(err).To(BeNil()) + + Expect(br.Status.GetCondition(buildapi.Succeeded).Status).To(Equal(corev1.ConditionFalse)) + Expect(br.Status.GetCondition(buildapi.Succeeded).Reason).To(Equal("VulnerabilitiesFound")) + Expect(br.Status.GetCondition(buildapi.Succeeded).Message).To(ContainSubstring("Vulnerabilities have been found in the image")) + }) + }) + + Context("When a buildrun with vulnerability scan is defined", func() { + BeforeEach(func() { + buildSample = []byte(test.MinimalBuild) + buildRunSample = []byte(test.MinimalBuildRunWithVulnerabilityScan) + }) + + It("should fail the builRun with a Reason", func() { + + Expect(tb.CreateBuild(buildObject)).To(BeNil()) + + buildObject, err = tb.GetBuildTillValidation(buildObject.Name) + Expect(err).To(BeNil()) + + Expect(tb.CreateBR(buildRunObject)).To(BeNil()) + + br, err := tb.GetBRTillCompletion(buildRunObject.Name) + Expect(err).To(BeNil()) + + Expect(br.Status.GetCondition(buildapi.Succeeded).Status).To(Equal(corev1.ConditionFalse)) + Expect(br.Status.GetCondition(buildapi.Succeeded).Reason).To(Equal("VulnerabilitiesFound")) + Expect(br.Status.GetCondition(buildapi.Succeeded).Message).To(ContainSubstring("Vulnerabilities have been found in the image")) + }) + }) + + Context("When both build and buildrun with vulnerability scan is defined", func() { + BeforeEach(func() { + buildSample = []byte(test.BuildWithDisableVulnerabilityScan) + buildRunSample = []byte(test.MinimalBuildRunWithVulnerabilityScan) + }) + + It("should override the vulnerability settings from builRun", func() { + + Expect(tb.CreateBuild(buildObject)).To(BeNil()) + + buildObject, err = tb.GetBuildTillValidation(buildObject.Name) + Expect(err).To(BeNil()) + + Expect(tb.CreateBR(buildRunObject)).To(BeNil()) + + br, err := tb.GetBRTillCompletion(buildRunObject.Name) + Expect(err).To(BeNil()) + + Expect(br.Status.GetCondition(buildapi.Succeeded).Status).To(Equal(corev1.ConditionFalse)) + Expect(br.Status.GetCondition(buildapi.Succeeded).Reason).To(Equal("VulnerabilitiesFound")) + Expect(br.Status.GetCondition(buildapi.Succeeded).Message).To(ContainSubstring("Vulnerabilities have been found in the image")) + }) + }) + + Context("When a standalone buildrun with vulnerability scan is defined", func() { + var standAloneBuildRunSample []byte + var standaloneBuildRunObject *buildapi.BuildRun + + BeforeEach(func() { + standAloneBuildRunSample = []byte(test.OneOffBuildRunWithVulnerabilityScan) + standaloneBuildRunObject, err = tb.Catalog.LoadStandAloneBuildRunWithNameAndStrategy(BUILDRUN+tb.Namespace+"-standalone", cbsObject, standAloneBuildRunSample) + Expect(err).To(BeNil()) + }) + + It("should fail the builRun with a Reason", func() { + + Expect(tb.CreateBR(standaloneBuildRunObject)).To(BeNil()) + + br, err := tb.GetBRTillCompletion(standaloneBuildRunObject.Name) + Expect(err).To(BeNil()) + + Expect(br.Status.GetCondition(buildapi.Succeeded).Status).To(Equal(corev1.ConditionFalse)) + Expect(br.Status.GetCondition(buildapi.Succeeded).Reason).To(Equal("VulnerabilitiesFound")) + Expect(br.Status.GetCondition(buildapi.Succeeded).Message).To(ContainSubstring("Vulnerabilities have been found in the image")) + }) + }) + +}) diff --git a/test/utils/v1beta1/environment.go b/test/utils/v1beta1/environment.go index df541a02f3..1858769543 100644 --- a/test/utils/v1beta1/environment.go +++ b/test/utils/v1beta1/environment.go @@ -90,7 +90,7 @@ func NewTestBuild() (*TestBuild, error) { return &TestBuild{ // TODO: interval and timeout can be configured via ENV vars Interval: time.Second * 3, - TimeOut: time.Second * 180, + TimeOut: time.Second * 300, KubeConfig: restConfig, Clientset: kubeConfig, Namespace: testNamespace, diff --git a/test/v1beta1_samples/build_samples.go b/test/v1beta1_samples/build_samples.go index 289a33bd50..ea96c9a2f0 100644 --- a/test/v1beta1_samples/build_samples.go +++ b/test/v1beta1_samples/build_samples.go @@ -493,6 +493,45 @@ spec: image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app ` +// BuildWithVulnerabilityScanOptions defines a Build with vulnerability scan options +const BuildWithVulnerabilityScanOptions = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + strategy: + kind: ClusterBuildStrategy + output: + vulnerabilityScan: + enabled: true + fail: true + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithVulnerabilityScanOptions defines a Build with vulnerability scan options +const BuildWithDisableVulnerabilityScan = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + strategy: + kind: ClusterBuildStrategy + output: + vulnerabilityScan: + enabled: false + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithRestrictedParam defines a Build using params that are reserved only +// for shipwright +const MinimalBuild = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + // BuildWithUndefinedParameter defines a param that was not declared under the // strategy parameters const BuildWithUndefinedParam = ` diff --git a/test/v1beta1_samples/buildrun_samples.go b/test/v1beta1_samples/buildrun_samples.go index cb88c40e09..13b8a4a39c 100644 --- a/test/v1beta1_samples/buildrun_samples.go +++ b/test/v1beta1_samples/buildrun_samples.go @@ -213,6 +213,39 @@ spec: name: foobar ` +// MinimalBuildRunWithVulnerabilityScan defines a BuildRun with +// an override for the Build Output +const MinimalBuildRunWithVulnerabilityScan = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + vulnerabilityScan: + enabled: true + fail: true + build: + name: foobar +` + +// MinimalBuildRunWithVulnerabilityScan defines a BuildRun with +// an override for the Build Output +const OneOffBuildRunWithVulnerabilityScan = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + build: + spec: + strategy: + kind: ClusterBuildStrategy + name: buildah + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + vulnerabilityScan: + enabled: true + fail: true +` + // MinimalBuildRunRetention defines a minimal BuildRun // with a reference used to test retention fields const MinimalBuildRunRetention = ` diff --git a/test/v1beta1_samples/clusterbuildstrategy_samples.go b/test/v1beta1_samples/clusterbuildstrategy_samples.go index 3b5bfec33b..2efae5a8d9 100644 --- a/test/v1beta1_samples/clusterbuildstrategy_samples.go +++ b/test/v1beta1_samples/clusterbuildstrategy_samples.go @@ -120,6 +120,41 @@ spec: mountPath: /var/lib/containers/storage ` +// ClusterBuildStrategyForVulnerabilityScanning is a strategy that does nothing and has no dependencies +const ClusterBuildStrategyForVulnerabilityScanning = ` +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: crane-pull +spec: + steps: + - name: crane-pull + image: gcr.io/go-containerregistry/crane:latest + workingDir: $(params.shp-source-root) + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + command: + - crane + args: + - pull + - "--format=tarball" + - "ghcr.io/shipwright-io/shipwright-samples/node:12" + - "$(params.shp-output-directory)/image.tar" + resources: + limits: + cpu: 250m + memory: 128Mi + requests: + cpu: 250m + memory: 128Mi +` + // ClusterBuildStrategySingleStepKaniko is a cluster build strategy based on // Kaniko, which is very close to the actual Kaniko build strategy example in // the project