diff --git a/Makefile b/Makefile index f87c79e71..486273b40 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ TEST_NAMESPACE ?= default TEKTON_VERSION ?= v0.44.0 # E2E test flags -TEST_E2E_FLAGS ?= -p --randomize-all -timeout=1h -trace -v +TEST_E2E_FLAGS ?= -r -p --randomize-all -timeout=1h -trace -v # E2E test service account name to be used for the build runs, can be set to generated to use the generated service account feature TEST_E2E_SERVICEACCOUNT_NAME ?= pipeline @@ -222,7 +222,7 @@ test-e2e-plain: ginkgo TEST_E2E_SERVICEACCOUNT_NAME=${TEST_E2E_SERVICEACCOUNT_NAME} \ TEST_E2E_TIMEOUT_MULTIPLIER=${TEST_E2E_TIMEOUT_MULTIPLIER} \ TEST_E2E_VERIFY_TEKTONOBJECTS=${TEST_E2E_VERIFY_TEKTONOBJECTS} \ - $(GINKGO) ${TEST_E2E_FLAGS} test/e2e + $(GINKGO) ${TEST_E2E_FLAGS} test/e2e/ .PHONY: test-e2e-kind-with-prereq-install test-e2e-kind-with-prereq-install: ginkgo install-controller-kind install-strategies test-e2e-plain @@ -270,7 +270,7 @@ install-controller-kind: install-apis .PHONY: install-strategies install-strategies: install-apis - kubectl apply -R -f samples/buildstrategy/ + kubectl apply -R -f samples/v1beta1/buildstrategy/ .PHONY: local local: install-strategies diff --git a/docs/build.md b/docs/build.md index cb6a5b934..7afc85fbb 100644 --- a/docs/build.md +++ b/docs/build.md @@ -25,14 +25,14 @@ SPDX-License-Identifier: Apache-2.0 A `Build` resource allows the user to define: - source -- sources (**this is deprecated, and will be removed in a future release**) +- trigger - strategy -- params -- builder -- dockerfile +- paramValues - output +- timeout - env - retention +- volumes A `Build` is available within a namespace. @@ -44,10 +44,10 @@ The controller watches for: When the controller reconciles it: -- Validates if the referenced `StrategyRef` exists. -- Validates if the specified `params` exist on the referenced strategy parameters. It also validates if the `params` names collide with the Shipwright reserved names. +- Validates if the referenced `Strategy` exists. +- Validates if the specified `paramValues` exist on the referenced strategy parameters. It also validates if the `paramValues` names collide with the Shipwright reserved names. - Validates if the container `registry` output secret exists. -- Validates if the referenced `spec.source.url` endpoint exists. +- Validates if the referenced `spec.source.git.url` endpoint exists. ## Build Validations @@ -59,14 +59,14 @@ To prevent users from triggering `BuildRuns` (_execution of a Build_) that will | --- | --- | | BuildStrategyNotFound | The referenced namespace-scope strategy doesn't exist. | | ClusterBuildStrategyNotFound | The referenced cluster-scope strategy doesn't exist. | -| SetOwnerReferenceFailed | Setting ownerreferences between a Build and a BuildRun failed. This status is triggered when using the `build.shipwright.io/build-run-deletion` annotation in a Build. | +| SetOwnerReferenceFailed | Setting ownerreferences between a Build and a BuildRun failed. This status is triggered when you set the `spec.retention.atBuildDeletion` to true in a Build. | | SpecSourceSecretRefNotFound | The secret used to authenticate to git doesn't exist. | | SpecOutputSecretRefNotFound | The secret used to authenticate to the container registry doesn't exist. | | SpecBuilderSecretRefNotFound | The secret used to authenticate the container registry doesn't exist.| | MultipleSecretRefNotFound | More than one secret is missing. At the moment, only three paths on a Build can specify a secret. | -| RestrictedParametersInUse | One or many defined `params` are colliding with Shipwright reserved parameters. See [Defining Params](#defining-paramvalues) for more information. | -| UndefinedParameter | One or many defined `params` are not defined in the referenced strategy. Please ensure that the strategy defines them under its `spec.parameters` list. | -| RemoteRepositoryUnreachable | The defined `spec.source.url` was not found. This validation only takes place for HTTP/HTTPS protocols. | +| RestrictedParametersInUse | One or many defined `paramValues` are colliding with Shipwright reserved parameters. See [Defining Params](#defining-paramvalues) for more information. | +| UndefinedParameter | One or many defined `paramValues` are not defined in the referenced strategy. Please ensure that the strategy defines them under its `spec.parameters` list. | +| RemoteRepositoryUnreachable | The defined `spec.source.git.url` was not found. This validation only takes place for HTTP/HTTPS protocols. | | BuildNameInvalid | The defined `Build` name (`metadata.name`) is invalid. The `Build` name should be a [valid label value](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set). | | SpecEnvNameCanNotBeBlank | Indicates that the name for a user-provided environment variable is blank. | | SpecEnvValueCanNotBeBlank | Indicates that the value for a user-provided environment variable is blank. | @@ -76,24 +76,21 @@ To prevent users from triggering `BuildRuns` (_execution of a Build_) that will The `Build` definition supports the following fields: - Required: - - [`apiVersion`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields) - Specifies the API version, for example `shipwright.io/v1alpha1`. + - [`apiVersion`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields) - Specifies the API version, for example `shipwright.io/v1beta1`. - [`kind`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields) - Specifies the Kind type, for example `Build`. - [`metadata`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields) - Metadata that identify the custom resource instance, especially the name of the `Build`, and in which [namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) you place it. **Note**: You should use your own namespace, and not put your builds into the shipwright-build namespace where Shipwright's system components run. - `spec.source` - Refers to the location of the source code, for example a Git repository or source bundle image. - `spec.strategy` - Refers to the `BuildStrategy` to be used, see the [examples](../samples/buildstrategy) - - `spec.builder.image` - Refers to the image containing the build tools to build the source code. (_Use this path for Dockerless strategies, this is just required for `source-to-image` buildStrategy_) **This field has been deprecated, and will be removed in a future release.** - `spec.output`- Refers to the location where the generated image would be pushed. - - `spec.output.credentials.name`- Reference an existing secret to get access to the container registry. + - `spec.output.pushSecret`- Reference an existing secret to get access to the container registry. - Optional: - `spec.paramValues` - Refers to a name-value(s) list to specify values for `parameters` defined in the `BuildStrategy`. - - `spec.dockerfile` - Path to a Dockerfile to be used for building an image. (_Use this path for strategies that require a Dockerfile_). **This field has been deprecated, and will be removed in a future release.** - - `spec.sources` - [Sources](#sources) describes a slice of artifacts that will be imported into the project context before the actual build process starts. **This field has been deprecated, and will be removed in a future release.** - `spec.timeout` - Defines a custom timeout. The value needs to be parsable by [ParseDuration](https://golang.org/pkg/time/#ParseDuration), for example, `5m`. The default is ten minutes. You can overwrite the value in the `BuildRun`. - - `metadata.annotations[build.shipwright.io/build-run-deletion]` - Defines if delete all related BuildRuns when deleting the Build. The default is `false`. - `spec.output.annotations` - Refers to a list of `key/value` that could be used to [annotate](https://github.com/opencontainers/image-spec/blob/main/annotations.md) the output image. - `spec.output.labels` - Refers to a list of `key/value` that could be used to label the output image. - `spec.env` - Specifies additional environment variables that should be passed to the build container. The available variables depend on the tool that is being used by the chosen build strategy. + - `spec.retention.atBuildDeletion` - Defines if all related BuildRuns needs to be deleted when deleting the Build. The default is false. - `spec.retention.ttlAfterFailed` - Specifies the duration for which a failed buildrun can exist. - `spec.retention.ttlAfterSucceeded` - Specifies the duration for which a successful buildrun can exist. - `spec.retention.failedLimit` - Specifies the number of failed buildrun that can exist. @@ -103,19 +100,17 @@ The `Build` definition supports the following fields: A `Build` resource can specify a Git repository or bundle image source, together with other parameters like: -- `source.url` - Specify the source location using a Git repository. -- `source.bundleContainer.image` - Specify a source bundle container image to be used as the source. -- `source.bundleContainer.prune` - Configure whether the source bundle image should be deleted after the source was obtained (defaults to `Never`, other option is `AfterPull` to delete the image after a successful image pull). -- `source.credentials.name` - For private repositories or registries, the name references a secret in the namespace that contains the SSH private key or Docker access credentials, respectively. -- `source.revision` - A specific revision to select from the source repository, this can be a commit, tag or branch name. If not defined, it will fallback to the Git repository default branch. +- `source.git.url` - Specify the source location using a Git repository. +- `source.git.cloneSecret` - For private repositories or registries, the name references a secret in the namespace that contains the SSH private key or Docker access credentials, respectively. +- `source.git.revision` - A specific revision to select from the source repository, this can be a commit, tag or branch name. If not defined, it will fallback to the Git repository default branch. - `source.contextDir` - For repositories where the source code is not located at the root folder, you can specify this path here. By default, the Build controller does not validate that the Git repository exists. If the validation is desired, users can explicitly define the `build.shipwright.io/verify.repository` annotation with `true`. For example: -Example of a `Build` with the **build.shipwright.io/verify.repository** annotation to enable the `spec.source.url` validation. +Example of a `Build` with the **build.shipwright.io/verify.repository** annotation to enable the `spec.source.git.url` validation. ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildah-golang-build @@ -123,63 +118,67 @@ metadata: build.shipwright.io/verify.repository: "true" spec: source: - url: https://github.com/shipwright-io/sample-go + git: + url: https://github.com/shipwright-io/sample-go contextDir: docker-build ``` -_Note_: The Build controller only validates two scenarios. The first one is when the endpoint uses an `http/https` protocol. The second one is when an `ssh` protocol such as `git@` has been defined but a referenced secret, such as `source.credentials.name`, has not been provided. +_Note_: The Build controller only validates two scenarios. The first one is when the endpoint uses an `http/https` protocol. The second one is when an `ssh` protocol such as `git@` has been defined but a referenced secret, such as `source.git.cloneSecret`, has not been provided. Example of a `Build` with a source with **credentials** defined by the user. ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildpack-nodejs-build spec: source: - url: https://github.com/sclorg/nodejs-ex - credentials: - name: source-repository-credentials + git: + url: https://github.com/sclorg/nodejs-ex + cloneSecret: source-repository-credentials ``` Example of a `Build` with a source that specifies a specific subfolder on the repository. ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildah-custom-context-dockerfile spec: source: - url: https://github.com/SaschaSchwarze0/npm-simple + git: + url: https://github.com/SaschaSchwarze0/npm-simple contextDir: renamed ``` Example of a `Build` that specifies the tag `v0.1.0` for the git repository: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildah-golang-build spec: source: - url: https://github.com/shipwright-io/sample-go + git: + url: https://github.com/shipwright-io/sample-go + revision: v0.1.0 contextDir: docker-build - revision: v0.1.0 ``` Example of a `Build` that specifies environment variables: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildah-golang-build spec: source: - url: https://github.com/shipwright-io/sample-go + git: + url: https://github.com/shipwright-io/sample-go contextDir: docker-build env: - name: EXAMPLE_VAR_1 @@ -192,13 +191,14 @@ Example of a `Build` that uses the Kubernetes Downward API to expose a `Pod` field as an environment variable: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildah-golang-build spec: source: - url: https://github.com/shipwright-io/sample-go + git: + url: https://github.com/shipwright-io/sample-go contextDir: docker-build env: - name: POD_NAME @@ -211,13 +211,14 @@ Example of a `Build` that uses the Kubernetes Downward API to expose a `Container` field as an environment variable: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildah-golang-build spec: source: - url: https://github.com/shipwright-io/sample-go + git: + url: https://github.com/shipwright-io/sample-go contextDir: docker-build env: - name: MEMORY_LIMIT @@ -241,7 +242,7 @@ A `Build` resource can specify the `BuildStrategy` to use, these are: Defining the strategy is straightforward. You define the `name` and the `kind`. For example: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildpack-nodejs-build @@ -271,7 +272,7 @@ In general, _paramValues_ are tightly bound to Strategy _parameters_. Please mak The [BuildKit sample `BuildStrategy`](../samples/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml) contains various parameters. Two of them are outlined here: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: ClusterBuildStrategy metadata: name: buildkit @@ -287,14 +288,14 @@ spec: type: string default: registry ... - buildSteps: + steps: ... ``` The `cache` parameter is a simple string. You can provide it like this in your Build: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: a-build @@ -327,7 +328,7 @@ data: You reference the ConfigMap as a parameter value like this: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: a-build @@ -350,7 +351,7 @@ spec: The `build-args` parameter is defined as an array. In the BuildKit strategy, you use `build-args` to set the [`ARG` values in the Dockerfile](https://docs.docker.com/engine/reference/builder/#arg), specified as key-value pairs separated by an equals sign, for example, `NODE_VERSION=16`. Your Build then looks like this (the value for `cache` is retained to outline how multiple _paramValue_ can be set): ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: a-build @@ -376,7 +377,7 @@ spec: Like simple values, you can also reference ConfigMaps and Secrets for every item in the array. Example: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: a-build @@ -419,10 +420,10 @@ Here, we pass three items in the `build-args` array: **Note: Builder and Dockerfile options are deprecated, and will be removed in a future release.** -In the `Build` resource, you use the `spec.builder` or `spec.dockerfile` parameters to specify the image that contains the tools to build the final image. For example, the following Build definition specifies a `Dockerfile` image. +In the `Build` resource, you use the parameters (`spec.paramValues`) to specify the image that contains the tools to build the final image. For example, the following Build definition specifies a `Dockerfile` image. ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildah-golang-build @@ -433,13 +434,15 @@ spec: strategy: name: buildah kind: ClusterBuildStrategy - dockerfile: Dockerfile + paramValues: + - name: dockerfile + value: Dockerfile ``` Another example is when the user chooses the `builder` image for a specific language as part of the `source-to-image` buildStrategy: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: s2i-nodejs-build @@ -450,8 +453,9 @@ spec: strategy: name: source-to-image kind: ClusterBuildStrategy - builder: - image: docker.io/centos/nodejs-10-centos7 + paramValues: + - name: builder-image + value: "docker.io/centos/nodejs-10-centos7" ``` ### Defining the Output @@ -463,19 +467,21 @@ A `Build` resource can specify the output where it should push the image. For ex For example, the user specifies a public registry: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: s2i-nodejs-build spec: source: - url: https://github.com/shipwright-io/sample-nodejs + git: + url: https://github.com/shipwright-io/sample-nodejs contextDir: source-build/ strategy: name: source-to-image kind: ClusterBuildStrategy - builder: - image: docker.io/centos/nodejs-10-centos7 + paramValues: + - name: builder-image + value: "docker.io/centos/nodejs-10-centos7" output: image: image-registry.openshift-image-registry.svc:5000/build-examples/nodejs-ex ``` @@ -483,45 +489,47 @@ spec: Another example is when the user specifies a private registry: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: s2i-nodejs-build spec: source: - url: https://github.com/shipwright-io/sample-nodejs + git: + url: https://github.com/shipwright-io/sample-nodejs contextDir: source-build/ strategy: name: source-to-image kind: ClusterBuildStrategy - builder: - image: docker.io/centos/nodejs-10-centos7 + paramValues: + - name: builder-image + value: "docker.io/centos/nodejs-10-centos7" output: image: us.icr.io/source-to-image-build/nodejs-ex - credentials: - name: icr-knbuild + pushSecret: icr-knbuild ``` Example of user specifies image annotations and labels: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: s2i-nodejs-build spec: source: - url: https://github.com/shipwright-io/sample-nodejs + git: + url: https://github.com/shipwright-io/sample-nodejs contextDir: source-build/ strategy: name: source-to-image kind: ClusterBuildStrategy - builder: - image: docker.io/centos/nodejs-10-centos7 + paramValues: + - name: builder-image + value: "docker.io/centos/nodejs-10-centos7" output: image: us.icr.io/source-to-image-build/nodejs-ex - credentials: - name: icr-knbuild + pushSecret: icr-knbuild annotations: "org.opencontainers.image.source": "https://github.com/org/repo" "org.opencontainers.image.url": "https://my-company.com/images" @@ -548,6 +556,7 @@ A `Build` resource can specify how long a completed BuildRun can exist and the n As part of the retention parameters, we have the following fields: +- `retention.atBuildDeletion` - Defines if all related BuildRuns needs to be deleted when deleting the Build. The default is false. - `retention.succeededLimit` - Defines number of succeeded BuildRuns for a Build that can exist. - `retention.failedLimit` - Defines number of failed BuildRuns for a Build that can exist. - `retention.ttlAfterFailed` - Specifies the duration for which a failed buildrun can exist. @@ -556,13 +565,14 @@ As part of the retention parameters, we have the following fields: An example of a user using both TTL and Limit retention fields. In case of such a configuration, BuildRun will get deleted once the first criteria is met. ```yaml - apiVersion: shipwright.io/v1alpha1 + apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: build-retention-ttl spec: source: - url: "https://github.com/shipwright-io/sample-go" + git: + url: "https://github.com/shipwright-io/sample-go" contextDir: docker-build strategy: kind: ClusterBuildStrategy @@ -579,8 +589,6 @@ An example of a user using both TTL and Limit retention fields. In case of such ### Defining Volumes -**Note: The `spec.volumes[].description` field is deprecated, and will be removed in a future release.** - `Builds` can declare `volumes`. They must override `volumes` defined by the according `BuildStrategy`. If a `volume` is not `overridable` then the `BuildRun` will eventually fail. @@ -590,17 +598,20 @@ all the usual `volumeSource` types are supported. Here is an example of `Build` object that overrides `volumes`: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: build-name spec: source: - url: https://github.com/example/url + git: + url: https://github.com/example/url strategy: name: buildah kind: ClusterBuildStrategy - dockerfile: Dockerfile + paramValues: + - name: dockerfile + value: Dockerfile output: image: registry/namespace/image:latest volumes: @@ -618,14 +629,14 @@ Using the triggers, you can submit `BuildRun` instances when certain events happ The types of events under watch are defined on the `.spec.trigger` attribute, please consider the following example: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build spec: source: - url: https://github.com/shipwright-io/sample-go + git: + url: https://github.com/shipwright-io/sample-go + cloneSecret: webhook-secret contextDir: docker-build - credentials: - name: webhook-secret trigger: when: [] ``` @@ -634,21 +645,22 @@ Certain types of events will use attributes defined on `.spec.source` to complet #### GitHub -The GitHub type is meant to react upon events coming from GitHub WebHook interface, the events are compared against the existing `Build` resources, and therefore it can identify the `Build` objects based on `.spec.source.url` combined with the attributes on `.spec.trigger.when[].github`. +The GitHub type is meant to react upon events coming from GitHub WebHook interface, the events are compared against the existing `Build` resources, and therefore it can identify the `Build` objects based on `.spec.source.git.url` combined with the attributes on `.spec.trigger.when[].github`. To identify a given `Build` object, the first criteria is the repository URL, and then the branch name listed on the GitHub event payload must also match. Following the criteria: - First, the branch name is checked against the `.spec.trigger.when[].github.branches` entries -- If the `.spec.trigger.when[].github.branches` is empty, the branch name is compared against `.spec.source.revision` -- If `spec.source.revision` is empty, the default revision name is used ("main") +- If the `.spec.trigger.when[].github.branches` is empty, the branch name is compared against `.spec.source.git.revision` +- If `spec.source.git.revision` is empty, the default revision name is used ("main") -The following snippet shows a configuration machting `Push` and `PullRequest` events on the `main` branch, for example: +The following snippet shows a configuration matching `Push` and `PullRequest` events on the `main` branch, for example: ```yaml # [...] spec: source: - url: https://github.com/shipwright-io/sample-go + git: + url: https://github.com/shipwright-io/sample-go trigger: when: - name: push and pull-request on the main branch @@ -712,43 +724,17 @@ spec: name: tekton-pipeline-name ``` -### Sources +## BuildRun Deletion -**Note: This feature has been deprecated, and will be removed in a future release**. - -Sources represent remote artifacts, as in external entities added to the build context before the actual Build starts. Therefore, you may employ `.spec.sources` to download artifacts from external repositories. +A `Build` can automatically delete a related `BuildRun`. To enable this feature set the `spec.retention.atBuildDeletion` to `true` in the `Build` instance. The default value is set to `false`. See an example of how to define this field: ```yaml -apiVersion: shipwright.io/v1alpha1 -kind: Build -metadata: - name: nodejs-ex -spec: - sources: - - name: project-logo - url: https://gist.github.com/project/image.png -``` - -Under `.spec.sources` are the following attributes: - -- `.name`: represents the name of the resource, required attribute. -- `.url`: universal resource location (URL), required attribute. - -When downloading artifacts, the process is executed in the same directory where the application source-code is located, by default `/workspace/source`. - -Additionally, we plan to keep evolving `.spec.sources` by adding more types of remote data declaration. This API field works as an extension point to support external and internal resource locations. - -At this initial stage, authentication is not supported; therefore, you can only download from sources without this mechanism in place. - -## BuildRun deletion - -A `Build` can automatically delete a related `BuildRun`. To enable this feature set the `build.shipwright.io/build-run-deletion` annotation to `true` in the `Build` instance. This annotation is not present in a `Build` definition by default. See an example of how to define this annotation: - -```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: kaniko-golang-build - annotations: - build.shipwright.io/build-run-deletion: "true" -``` +spec: + retention: + atBuildDeletion: true + # [...] +``` \ No newline at end of file diff --git a/docs/buildrun.md b/docs/buildrun.md index 093ac7930..ba3c4cb62 100644 --- a/docs/buildrun.md +++ b/docs/buildrun.md @@ -9,8 +9,8 @@ SPDX-License-Identifier: Apache-2.0 - [Overview](#overview) - [BuildRun Controller](#buildrun-controller) - [Configuring a BuildRun](#configuring-a-buildrun) - - [Defining the BuildRef](#defining-the-buildref) - - [Defining the BuildSpec](#defining-the-buildspec) + - [Defining the Build Reference](#defining-the-buildref) + - [Defining the Build Specification](#defining-the-buildspec) - [Defining ParamValues](#defining-paramvalues) - [Defining the ServiceAccount](#defining-the-serviceaccount) - [Defining Retention Parameters](#defining-retention-parameters) @@ -28,7 +28,7 @@ SPDX-License-Identifier: Apache-2.0 ## Overview -The resource `BuildRun` (`buildruns.shipwright.io/v1alpha1`) is the build process of a `Build` resource definition executed in Kubernetes. +The resource `BuildRun` (`buildruns.shipwright.io/v1beta1`) is the build process of a `Build` resource definition executed in Kubernetes. A `BuildRun` resource allows the user to define: @@ -57,55 +57,57 @@ When the controller reconciles it: The `BuildRun` definition supports the following fields: - Required: - - [`apiVersion`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields) - Specifies the API version, for example `shipwright.io/v1alpha1`. + - [`apiVersion`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields) - Specifies the API version, for example `shipwright.io/v1beta1`. - [`kind`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields) - Specifies the Kind type, for example `BuildRun`. - [`metadata`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields) - Metadata that identify the CRD instance, for example the name of the `BuildRun`. - Optional: - - `spec.buildRef` - Specifies an existing `Build` resource instance to use. It cannot be used together with `buildSpec`. - - `spec.buildSpec` - Specifies an embedded (transient) Build resource to use. It cannot be used together with `buildRef`. + - `spec.build.name` - Specifies an existing `Build` resource instance to use. + - `spec.build.spec` - Specifies an embedded (transient) Build resource to use. - `spec.serviceAccount` - Refers to the SA to use when building the image. (_defaults to the `default` SA_) - `spec.timeout` - Defines a custom timeout. The value needs to be parsable by [ParseDuration](https://golang.org/pkg/time/#ParseDuration), for example, `5m`. The value overwrites the value that is defined in the `Build`. - `spec.paramValues` - Refers to a name-value(s) list to specify values for `parameters` defined in the `BuildStrategy`. This value overwrites values defined with the same name in the Build. - `spec.output.image` - Refers to a custom location where the generated image would be pushed. The value will overwrite the `output.image` value defined in `Build`. ( Note: other properties of the output, for example, the credentials, cannot be specified in the buildRun spec. ) - - `spec.output.credentials.name` - Reference an existing secret to get access to the container registry. This secret will be added to the service account along with the ones requested by the `Build`. + - `spec.output.pushSecret` - Reference an existing secret to get access to the container registry. This secret will be added to the service account along with the ones requested by the `Build`. - `spec.env` - Specifies additional environment variables that should be passed to the build container. Overrides any environment variables that are specified in the `Build` resource. The available variables depend on the tool used by the chosen build strategy. -_Note:_ The `BuildRef` and `BuildSpec` are mutually exclusive. Furthermore, the overrides for `timeout`, `paramValues`, `output`, and `env` can only be combined with `buildRef`, but **not** with `buildSpec`. +_Note:_ The `spec.build.name` and `spec.build.spec` are mutually exclusive. Furthermore, the overrides for `timeout`, `paramValues`, `output`, and `env` can only be combined with `spec.build.name`, but **not** with `spec.build.spec`. -### Defining the BuildRef +### Defining the Build Reference A `BuildRun` resource can reference a `Build` resource, that indicates what image to build. For example: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buildpack-nodejs-buildrun-namespaced spec: - buildRef: + build: name: buildpack-nodejs-build-namespaced ``` -### Defining the BuildSpec +### Defining the Build Specification -Alternatively to `BuildRef`, a complete `BuildSpec` can be embedded into the `BuildRun` for the build. +A complete `BuildSpec` can be embedded into the `BuildRun` for the build. ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: standalone-buildrun spec: - buildSpec: - source: - url: https://github.com/shipwright-io/sample-go.git - contextDir: source-build - strategy: - kind: ClusterBuildStrategy - name: buildpacks-v3 - output: - image: foo/bar:latest + build: + spec: + source: + git: + url: https://github.com/shipwright-io/sample-go.git + contextDir: source-build + strategy: + kind: ClusterBuildStrategy + name: buildpacks-v3 + output: + image: foo/bar:latest ``` ### Defining ParamValues @@ -116,7 +118,7 @@ For example, the following `BuildRun` overrides the value for _sleep-time_ param ```yaml --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: a-build @@ -134,13 +136,13 @@ spec: ... --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: a-buildrun namespace: a-namespace spec: - buildRef: + build: name: a-build paramValues: - name: cache @@ -154,18 +156,17 @@ See more about _paramValues_ usage in the related [Build](./build.md#defining-pa A `BuildRun` resource can define a serviceaccount to use. Usually this SA will host all related secrets referenced on the `Build` resource, for example: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buildpack-nodejs-buildrun-namespaced spec: - buildRef: + build: name: buildpack-nodejs-build-namespaced - serviceAccount: - name: pipeline + serviceAccount: pipeline ``` -You can also use set the `spec.serviceAccount.generate` path to `true`. This will generate the service account during runtime for you. The name of the generated service account is the name of the BuildRun. **This field is deprecated, and will be removed in a future release.** +You can also set the value of `spec.serviceAccount` to `".generate"`. This will generate the service account during runtime for you. The name of the generated service account is the same as that of the BuildRun. _**Note**_: When the service account is not defined, the `BuildRun` uses the `pipeline` service account if it exists in the namespace, and falls back to the `default` service account. @@ -181,12 +182,12 @@ As part of the buildrun retention parameters, we have the following fields: An example of a user using buildrun TTL parameters. ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buidrun-retention-ttl spec: - buildRef: + build: name: build-retention-ttl retention: ttlAfterFailed: 10m @@ -209,12 +210,12 @@ all the usual `volumeSource` types are supported. Here is an example of `BuildRun` object that overrides `volumes`: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buildrun-name spec: - buildRef: + build: name: build-name volumes: - name: volume-name @@ -231,7 +232,7 @@ When you cancel a `BuildRun`, the underlying `TaskRun` is marked as canceled per Example of canceling a `BuildRun`: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buildpack-nodejs-buildrun-namespaced @@ -259,12 +260,12 @@ We have two controllers that ensure that buildruns can be deleted automatically An example of a `BuildRun` that specifies environment variables: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buildpack-nodejs-buildrun-namespaced spec: - buildRef: + build: name: buildpack-nodejs-build-namespaced env: - name: EXAMPLE_VAR_1 @@ -277,12 +278,12 @@ Example of a `BuildRun` that uses the Kubernetes Downward API to expose a `Pod` field as an environment variable: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buildpack-nodejs-buildrun-namespaced spec: - buildRef: + build: name: buildpack-nodejs-build-namespaced env: - name: POD_NAME @@ -295,12 +296,12 @@ Example of a `BuildRun` that uses the Kubernetes Downward API to expose a `Container` field as an environment variable: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buildpack-nodejs-buildrun-namespaced spec: - buildRef: + build: name: buildpack-nodejs-build-namespaced env: - name: MEMORY_LIMIT @@ -370,22 +371,20 @@ The following table illustrates the different states a BuildRun can have under i | False | BuildNotFound | Yes | The related Build in the BuildRun was not found. | | False | BuildRunCanceled | Yes | The BuildRun and underlying TaskRun were canceled successfully. | | False | BuildRunNameInvalid | Yes | The defined `BuildRun` name (`metadata.name`) is invalid. The `BuildRun` name should be a [valid label value](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set). | -| False | BuildRunNoRefOrSpec | Yes | BuildRun does not have either `BuildRef` or `BuildSpec` defined. There is no connection to a Build specification. | -| False | BuildRunAmbiguousBuild | Yes | The defined `BuildRun` uses both `BuildRef` and `BuildSpec`. Only one of them is allowed at the same time.| -| False | BuildRunBuildFieldOverrideForbidden | Yes | The defined `BuildRun` uses an override (e.g. `timeout`, `paramValues`, `output`, or `env`) in combination with `BuildSpec`, which is not allowed. Use the `BuildSpec` to directly specify the respective value. | +| False | BuildRunNoRefOrSpec | Yes | BuildRun does not have either `spec.build.name` or `spec.build.spec` defined. There is no connection to a Build specification. | +| False | BuildRunAmbiguousBuild | Yes | The defined `BuildRun` uses both `spec.build.name` and `spec.build.spec`. Only one of them is allowed at the same time.| +| False | BuildRunBuildFieldOverrideForbidden | Yes | The defined `BuildRun` uses an override (e.g. `timeout`, `paramValues`, `output`, or `env`) in combination with `spec.build.spec`, which is not allowed. Use the `spec.build.spec` to directly specify the respective value. | | False | PodEvicted | Yes | The BuildRun Pod was evicted from the node it was running on. See [API-initiated Eviction](https://kubernetes.io/docs/concepts/scheduling-eviction/api-eviction/) and [Node-pressure Eviction](https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/) for more information. | _Note_: We heavily rely on the Tekton TaskRun [Conditions](https://github.com/tektoncd/pipeline/blob/main/docs/taskruns.md#monitoring-execution-status) for populating the BuildRun ones, with some exceptions. ### Understanding failed BuildRuns -[DEPRECATED] To make it easier for users to understand why did a BuildRun failed, users can infer the pod and container where the failure took place from the `status.failedAt` field. +To make it easier for users to understand why did a BuildRun failed, users can infer the pod and container where the failure took place from the `status.failureDetails` field. -In addition, the `status.conditions` hosts a compacted message under the' message' field that contains the `kubectl` command to trigger and retrieve the logs. +In addition, the `status.conditions` hosts a compacted message under the `message` field that contains the `kubectl` command to trigger and retrieve the logs. -Lastly, users can check the `status.failureDetails` field, which includes the same information available in the `status.failedAt` field, -as well as a human-readable error message and reason. -The message and reason are only included if the build strategy provides them. +The `status.failureDetails` field also includes a detailed failure reason and message, if the build strategy provides them. Example of failed BuildRun: diff --git a/docs/buildstrategies.md b/docs/buildstrategies.md index 1ddebb4f6..f97b9e3da 100644 --- a/docs/buildstrategies.md +++ b/docs/buildstrategies.md @@ -46,7 +46,7 @@ SPDX-License-Identifier: Apache-2.0 ## Overview -There are two types of strategies, the `ClusterBuildStrategy` (`clusterbuildstrategies.shipwright.io/v1alpha1`) and the `BuildStrategy` (`buildstrategies.shipwright.io/v1alpha1`). Both strategies define a shared group of steps, needed to fullfil the application build. +There are two types of strategies, the `ClusterBuildStrategy` (`clusterbuildstrategies.shipwright.io/v1beta1`) and the `BuildStrategy` (`buildstrategies.shipwright.io/v1beta1`). Both strategies define a shared group of steps, needed to fullfil the application build. A `ClusterBuildStrategy` is available cluster-wide, while a `BuildStrategy` is available within a namespace. @@ -271,7 +271,7 @@ Users defining _parameters_ under their strategies require to understand the fol - name: tool-args description: Parameters for the tool type: array - buildSteps: + steps: - name: a-step command: - some-tool @@ -299,7 +299,7 @@ The following example is from the [BuildKit sample build strategy](../samples/bu ```yaml --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: ClusterBuildStrategy metadata: name: buildkit @@ -322,7 +322,11 @@ spec: description: "The secrets to pass to the build. Values must be in the format ID=FILE_CONTENT." type: array defaults: [] - buildSteps: + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: "Dockerfile" + steps: ... - name: build-and-push image: moby/buildkit:nightly-rootless @@ -337,7 +341,7 @@ spec: set -euo pipefail # Prepare the file arguments - DOCKERFILE_PATH='$(params.shp-source-context)/$(build.dockerfile)' + DOCKERFILE_PATH='$(params.shp-source-context)/$(params.dockerfile)' DOCKERFILE_DIR="$(dirname "${DOCKERFILE_PATH}")" DOCKERFILE_NAME="$(basename "${DOCKERFILE_PATH}")" @@ -460,7 +464,7 @@ spec: - name: sample-parameter description: A sample parameter type: string - buildSteps: + steps: - name: sample-step command: - /bin/bash @@ -491,7 +495,7 @@ To securely pass a parameter value into a script-style argument, you can chose b - name: sample-parameter description: A sample parameter type: string - buildSteps: + steps: - name: sample-step env: - name: PARAM_SAMPLE_PARAMETER @@ -514,7 +518,7 @@ To securely pass a parameter value into a script-style argument, you can chose b - name: sample-parameter description: A sample parameter type: string - buildSteps: + steps: - name: sample-step command: - /bin/bash @@ -544,7 +548,7 @@ You can look at sample build strategies, such as [Buildpacks](../samples/buildst This information will be available in the `.status.output` section of the BuildRun. ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun # [...] status: @@ -567,7 +571,7 @@ Error details are only propagated if the build container terminates with a non-z This information will be available in the `.status.failureDetails` section of the BuildRun. ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun # [...] status: @@ -608,12 +612,12 @@ If the strategy admins would require to have multiple flavours of the same strat ```yaml --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: ClusterBuildStrategy metadata: name: kaniko-small spec: - buildSteps: + steps: - name: build-and-push image: gcr.io/kaniko-project/executor:v1.16.0 workingDir: $(params.shp-source-root) @@ -639,7 +643,7 @@ spec: - /kaniko/executor args: - --skip-tls-verify=true - - --dockerfile=$(build.dockerfile) + - --dockerfile=$(params.dockerfile) - --context=$(params.shp-source-context) - --destination=$(params.shp-output-image) - --snapshot-mode=redo @@ -651,13 +655,18 @@ spec: requests: cpu: 250m memory: 65Mi + parameters: + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: "Dockerfile" --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: ClusterBuildStrategy metadata: name: kaniko-medium spec: - buildSteps: + steps: - name: build-and-push image: gcr.io/kaniko-project/executor:v1.16.0 workingDir: $(params.shp-source-root) @@ -683,7 +692,7 @@ spec: - /kaniko/executor args: - --skip-tls-verify=true - - --dockerfile=$(build.dockerfile) + - --dockerfile=$(params.dockerfile) - --context=$(params.shp-source-context) - --destination=$(params.shp-output-image) - --snapshot-mode=redo @@ -695,24 +704,32 @@ spec: requests: cpu: 500m memory: 1Gi + parameters: + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: "Dockerfile" ``` The above provides more control and flexibility for the strategy admins. For `end-users`, all they need to do, is to reference the proper strategy. For example: ```yaml --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: kaniko-medium spec: source: - url: https://github.com/shipwright-io/sample-go + git: + url: https://github.com/shipwright-io/sample-go contextDir: docker-build strategy: name: kaniko kind: ClusterBuildStrategy - dockerfile: Dockerfile + paramValues: + - name: dockerfile + value: Dockerfile ``` ### How does Tekton Pipelines handle resources @@ -820,7 +837,7 @@ If we will apply the following resources: args: - bud - --tag=$(params.shp-output-image) - - --file=$(build.dockerfile) + - --file=$(params.dockerfile) - $(build.source.contextDir) resources: limits: @@ -947,12 +964,12 @@ Build steps can declare a `volumeMount`, which allows them to access volumes def Here is an example of `BuildStrategy` object that defines `volumes` and `volumeMount`s: ``` -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildStrategy metadata: name: buildah spec: - buildSteps: + steps: - name: build image: quay.io/containers/buildah:v1.27.0 workingDir: $(params.shp-source-root) @@ -962,7 +979,7 @@ spec: - --tls-verify=false - --layers - -f - - $(build.dockerfile) + - $(params.dockerfile) - -t - $(params.shp-output-image) - $(params.shp-source-context) @@ -973,4 +990,5 @@ spec: - name: varlibcontainers overridable: true emptyDir: {} + # ... ``` diff --git a/docs/development/authentication.md b/docs/development/authentication.md index 312b9be54..3913a50c2 100644 --- a/docs/development/authentication.md +++ b/docs/development/authentication.md @@ -105,29 +105,29 @@ Depending on the secret type, there are two ways of doing this: When using ssh auth, users should follow: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildah-golang-build spec: source: - url: git@gitlab.com:eduardooli/newtaxi.git - credentials: - name: secret-git-ssh-auth + git: + url: git@gitlab.com:eduardooli/newtaxi.git + cloneSecret: secret-git-ssh-auth ``` When using basic auth, users should follow: ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildah-golang-build spec: source: - url: https://gitlab.com/eduardooli/newtaxi.git - credentials: - name: secret-git-basic-auth + git: + url: https://gitlab.com/eduardooli/newtaxi.git + cloneSecret: secret-git-basic-auth ``` ## Authentication to container registries @@ -153,18 +153,17 @@ _Notes:_ The value of `PASSWORD` can be your user docker hub password, or an acc ### Usage of registry secret With the right secret in place (_note: Ensure creation of secret in the proper Kubernetes namespace_), users should reference it on their Build YAML definitions. -For container registries, the secret should be placed under the `spec.output.credentials` path. +For container registries, the secret should be placed under the `spec.output.pushSecret` path. ```yaml -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildah-golang-build ... output: image: docker.io/foobar/sample:latest - credentials: - name: + pushSecret: ``` ## References diff --git a/docs/development/testing.md b/docs/development/testing.md index 86025b458..740b4aa69 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -129,7 +129,7 @@ The following table contains a list of environment variables that will override |------------------------------------|--------------------------------|-----------------------------------------------------| | `TEST_IMAGE_REPO` | `spec.output.image` | Image repository for end-to-end tests | | `TEST_IMAGE_REPO_INSECURE` | `spec.output.insecure` | Flag whether the image repository is secure or not. | -| `TEST_IMAGE_REPO_SECRET` | `spec.output.credentials.name` | Container credentials secret name | +| `TEST_IMAGE_REPO_SECRET` | `spec.output.pushSecret` | Container credentials secret name | | `TEST_IMAGE_REPO_DOCKERCONFIGJSON` | _none_ | JSON payload equivalent to `~/.docker/config.json` | The contents of `TEST_IMAGE_REPO_DOCKERCONFIGJSON` can be obtained from [quay.io](https://quay.io) using a [robot account](https://docs.quay.io/glossary/robot-accounts.html). The JSON payload is for example: @@ -155,9 +155,9 @@ End-to-end tests can also be executed with the context of private Git repositori | Environment Variable | Path | Description | |-----------------------|--------------------------------|---------------------------------------| | `TEST_PRIVATE_REPO` | _none_ | Enable private repository e2e tests | -| `TEST_PRIVATE_GITHUB` | `spec.source.url` | Private URL, like `git@github.com` | -| `TEST_PRIVATE_GITLAB` | `spec.source.url` | Private URL, like `git@gitlab.com` | -| `TEST_SOURCE_SECRET` | `spec.source.credentials.name` | Private repository credentials | +| `TEST_PRIVATE_GITHUB` | `spec.source.git.url` | Private URL, like `git@github.com` | +| `TEST_PRIVATE_GITLAB` | `spec.source.git.url` | Private URL, like `git@gitlab.com` | +| `TEST_SOURCE_SECRET` | `spec.source.git.cloneSecret` | Private repository credentials | On using `TEST_SOURCE_SECRET`, the environment variable must contain the name of the Kubernetes Secret containing SSH private key, for given private Git repository. See the [docs](authentication.md) for more information about authentication methods in the Build. diff --git a/docs/tutorials/building_with_buildkit.md b/docs/tutorials/building_with_buildkit.md index 82975818f..36f392ecc 100644 --- a/docs/tutorials/building_with_buildkit.md +++ b/docs/tutorials/building_with_buildkit.md @@ -54,21 +54,21 @@ Let's apply our Build and wait for it to be ready: ```bash $ export REGISTRY_ORG= $ cat < $ cat < $ cat < 0 { b.Build.Status.Reason = build.BuildReasonPtr(build.BuildNameInvalid) - b.Build.Status.Message = pointer.String(strings.Join(errs, ", ")) + b.Build.Status.Message = ptr.To[string](strings.Join(errs, ", ")) } return nil diff --git a/pkg/validate/envvars.go b/pkg/validate/envvars.go index 6d116b78a..fb23a2d2e 100644 --- a/pkg/validate/envvars.go +++ b/pkg/validate/envvars.go @@ -10,7 +10,7 @@ import ( corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" ) @@ -47,13 +47,13 @@ func (e *Env) validate(envVar corev1.EnvVar) []error { if envVar.Name == "" { e.Build.Status.Reason = build.BuildReasonPtr(build.SpecEnvNameCanNotBeBlank) - e.Build.Status.Message = pointer.String("name for environment variable must not be blank") + e.Build.Status.Message = ptr.To[string]("name for environment variable must not be blank") allErrs = append(allErrs, fmt.Errorf("%s", *e.Build.Status.Message)) } if envVar.Value != "" && envVar.ValueFrom != nil { e.Build.Status.Reason = build.BuildReasonPtr(build.SpecEnvOnlyOneOfValueOrValueFromMustBeSpecified) - e.Build.Status.Message = pointer.String("only one of value or valueFrom must be specified") + e.Build.Status.Message = ptr.To[string]("only one of value or valueFrom must be specified") allErrs = append(allErrs, fmt.Errorf("%s", *e.Build.Status.Message)) } diff --git a/pkg/validate/envvars_test.go b/pkg/validate/envvars_test.go index 293de3e5a..f548238ea 100644 --- a/pkg/validate/envvars_test.go +++ b/pkg/validate/envvars_test.go @@ -11,7 +11,7 @@ import ( . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" "github.com/shipwright-io/build/pkg/validate" @@ -34,7 +34,7 @@ var _ = Describe("Env", func() { err := validate.NewEnv(b).ValidatePath(context.TODO()) Expect(err).To(HaveOccurred()) Expect(b.Status.Reason).To(Equal(build.BuildReasonPtr(build.SpecEnvNameCanNotBeBlank))) - Expect(b.Status.Message).To(Equal(pointer.String("name for environment variable must not be blank"))) + Expect(b.Status.Message).To(Equal(ptr.To[string]("name for environment variable must not be blank"))) }) It("should fail in case of specifying both value and valueFrom", func() { @@ -57,7 +57,7 @@ var _ = Describe("Env", func() { err := validate.NewEnv(b).ValidatePath(context.TODO()) Expect(err).To(HaveOccurred()) Expect(b.Status.Reason).To(Equal(build.BuildReasonPtr(build.SpecEnvOnlyOneOfValueOrValueFromMustBeSpecified))) - Expect(b.Status.Message).To(Equal(pointer.String("only one of value or valueFrom must be specified"))) + Expect(b.Status.Message).To(Equal(ptr.To[string]("only one of value or valueFrom must be specified"))) }) It("should pass in case no env var are set", func() { diff --git a/pkg/validate/ownerreferences.go b/pkg/validate/ownerreferences.go index 2a39c8fe8..65f8cc405 100644 --- a/pkg/validate/ownerreferences.go +++ b/pkg/validate/ownerreferences.go @@ -11,7 +11,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -44,7 +44,7 @@ func (o OwnerRef) ValidatePath(ctx context.Context) error { if index := o.validateBuildOwnerReference(buildRun.OwnerReferences); index == -1 { if err := controllerutil.SetControllerReference(o.Build, &buildRun, o.Scheme); err != nil { o.Build.Status.Reason = build.BuildReasonPtr(build.SetOwnerReferenceFailed) - o.Build.Status.Message = pointer.String(fmt.Sprintf("unexpected error when trying to set the ownerreference: %v", err)) + o.Build.Status.Message = ptr.To[string](fmt.Sprintf("unexpected error when trying to set the ownerreference: %v", err)) } if err = o.Client.Update(ctx, &buildRun); err != nil { return err diff --git a/pkg/validate/params_test.go b/pkg/validate/params_test.go index 66bc57c15..dc5f8072d 100644 --- a/pkg/validate/params_test.go +++ b/pkg/validate/params_test.go @@ -7,8 +7,7 @@ package validate_test import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" "github.com/shipwright-io/build/pkg/validate" @@ -23,7 +22,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { { Name: "string-param-with-default", Type: buildv1alpha1.ParameterTypeString, - Default: pointer.String("default value"), + Default: ptr.To[string]("default value"), }, { Name: "array-param-no-defaults", @@ -41,7 +40,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { { Name: "string-param-no-default", SingleValue: &buildv1alpha1.SingleValue{ - Value: pointer.String("a value"), + Value: ptr.To[string]("a value"), }, }, } @@ -92,7 +91,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { { Name: "string-param-with-default", SingleValue: &buildv1alpha1.SingleValue{ - Value: pointer.String(""), + Value: ptr.To[string](""), }, }, } @@ -143,7 +142,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { { Name: "string-param-no-default", SingleValue: &buildv1alpha1.SingleValue{ - Value: pointer.String("a value"), + Value: ptr.To[string]("a value"), }, }, { @@ -156,7 +155,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { { Name: "shp-source-context", SingleValue: &buildv1alpha1.SingleValue{ - Value: pointer.String("/my-source"), + Value: ptr.To[string]("/my-source"), }, }, } @@ -174,7 +173,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { { Name: "string-param-no-default", SingleValue: &buildv1alpha1.SingleValue{ - Value: pointer.String("a value"), + Value: ptr.To[string]("a value"), }, }, { @@ -191,7 +190,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { { Name: "non-existing-parameter", SingleValue: &buildv1alpha1.SingleValue{ - Value: pointer.String("my value"), + Value: ptr.To[string]("my value"), }, }, } @@ -211,7 +210,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { { Name: "string-param-no-default", SingleValue: &buildv1alpha1.SingleValue{ - Value: pointer.String("a value"), + Value: ptr.To[string]("a value"), ConfigMapValue: &buildv1alpha1.ObjectKeyRef{ Name: "a-config-map", Key: "a-key", @@ -225,7 +224,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { Name: "array-param-no-defaults", Values: []buildv1alpha1.SingleValue{ { - Value: pointer.String("a good item"), + Value: ptr.To[string]("a good item"), }, { ConfigMapValue: &buildv1alpha1.ObjectKeyRef{ @@ -257,7 +256,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { Name: "string-param-no-default", Values: []buildv1alpha1.SingleValue{ { - Value: pointer.String("an item"), + Value: ptr.To[string]("an item"), }, { ConfigMapValue: &buildv1alpha1.ObjectKeyRef{ @@ -273,7 +272,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { { Name: "array-param-no-defaults", SingleValue: &buildv1alpha1.SingleValue{ - Value: pointer.String("a value"), + Value: ptr.To[string]("a value"), }, }, } @@ -293,7 +292,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { { Name: "string-param-no-default", SingleValue: &buildv1alpha1.SingleValue{ - Value: pointer.String(" some value"), + Value: ptr.To[string](" some value"), }, }, { @@ -311,7 +310,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { Name: "array-param-no-defaults", Values: []buildv1alpha1.SingleValue{ { - Value: pointer.String("a good item"), + Value: ptr.To[string]("a good item"), }, { // the bad item without any value @@ -353,7 +352,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { Name: "array-param-no-defaults", Values: []buildv1alpha1.SingleValue{ { - Value: pointer.String("an item"), + Value: ptr.To[string]("an item"), }, { ConfigMapValue: &buildv1alpha1.ObjectKeyRef{ @@ -391,7 +390,7 @@ var _ = Describe("ValidateBuildRunParameters", func() { Name: "array-param-no-defaults", Values: []buildv1alpha1.SingleValue{ { - Value: pointer.String("an item"), + Value: ptr.To[string]("an item"), }, { SecretValue: &buildv1alpha1.ObjectKeyRef{ diff --git a/pkg/validate/secrets.go b/pkg/validate/secrets.go index 5e3692e0c..a124c3b0b 100644 --- a/pkg/validate/secrets.go +++ b/pkg/validate/secrets.go @@ -13,7 +13,7 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" @@ -43,7 +43,7 @@ func (s Credentials) ValidatePath(ctx context.Context) error { return err } else if apierrors.IsNotFound(err) { s.Build.Status.Reason = build.BuildReasonPtr(secretType) - s.Build.Status.Message = pointer.String(fmt.Sprintf("referenced secret %s not found", refSecret)) + s.Build.Status.Message = ptr.To[string](fmt.Sprintf("referenced secret %s not found", refSecret)) missingSecrets = append(missingSecrets, refSecret) } } @@ -53,7 +53,7 @@ func (s Credentials) ValidatePath(ctx context.Context) error { if len(missingSecrets) > 1 { s.Build.Status.Reason = build.BuildReasonPtr(build.MultipleSecretRefNotFound) - s.Build.Status.Message = pointer.String(fmt.Sprintf("missing secrets are %s", strings.Join(missingSecrets, ","))) + s.Build.Status.Message = ptr.To[string](fmt.Sprintf("missing secrets are %s", strings.Join(missingSecrets, ","))) } return nil } diff --git a/pkg/validate/sourceurl.go b/pkg/validate/sourceurl.go index c724d423b..7d9c21cae 100644 --- a/pkg/validate/sourceurl.go +++ b/pkg/validate/sourceurl.go @@ -8,7 +8,7 @@ import ( "context" "fmt" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" @@ -56,5 +56,5 @@ func (s SourceURLRef) ValidatePath(ctx context.Context) error { // MarkBuildStatus updates a Build Status fields func (s SourceURLRef) MarkBuildStatus(b *build.Build, reason build.BuildReason, msg string) { b.Status.Reason = build.BuildReasonPtr(reason) - b.Status.Message = pointer.String(msg) + b.Status.Message = ptr.To[string](msg) } diff --git a/pkg/validate/strategies.go b/pkg/validate/strategies.go index 56a24d253..4013c3127 100644 --- a/pkg/validate/strategies.go +++ b/pkg/validate/strategies.go @@ -10,7 +10,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" @@ -75,7 +75,7 @@ func (s Strategy) validateBuildStrategy(ctx context.Context, strategyName string return false, err } else if apierrors.IsNotFound(err) { s.Build.Status.Reason = build.BuildReasonPtr(build.BuildStrategyNotFound) - s.Build.Status.Message = pointer.String(fmt.Sprintf("buildStrategy %s does not exist in namespace %s", s.Build.Spec.Strategy.Name, s.Build.Namespace)) + s.Build.Status.Message = ptr.To[string](fmt.Sprintf("buildStrategy %s does not exist in namespace %s", s.Build.Spec.Strategy.Name, s.Build.Namespace)) return false, nil } return true, nil @@ -86,7 +86,7 @@ func (s Strategy) validateClusterBuildStrategy(ctx context.Context, strategyName return false, err } else if apierrors.IsNotFound(err) { s.Build.Status.Reason = build.BuildReasonPtr(build.ClusterBuildStrategyNotFound) - s.Build.Status.Message = pointer.String(fmt.Sprintf("clusterBuildStrategy %s does not exist", s.Build.Spec.Strategy.Name)) + s.Build.Status.Message = ptr.To[string](fmt.Sprintf("clusterBuildStrategy %s does not exist", s.Build.Spec.Strategy.Name)) return false, nil } return true, nil @@ -97,7 +97,7 @@ func (s Strategy) validateBuildParams(parameterDefinitions []build.Parameter) { if !valid { s.Build.Status.Reason = build.BuildReasonPtr(reason) - s.Build.Status.Message = pointer.String(message) + s.Build.Status.Message = ptr.To[string](message) } } @@ -106,6 +106,6 @@ func (s Strategy) validateBuildVolumes(strategyVolumes []build.BuildStrategyVolu if !valid { s.Build.Status.Reason = build.BuildReasonPtr(reason) - s.Build.Status.Message = pointer.String(message) + s.Build.Status.Message = ptr.To[string](message) } } diff --git a/pkg/validate/trigger.go b/pkg/validate/trigger.go index 562aafea1..bd3160f20 100644 --- a/pkg/validate/trigger.go +++ b/pkg/validate/trigger.go @@ -9,7 +9,7 @@ import ( build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" kerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) // Trigger implements the interface BuildPath with the objective of applying validations against the @@ -24,7 +24,7 @@ func (t *Trigger) validate(triggerWhen []build.TriggerWhen) []error { for _, when := range triggerWhen { if when.Name == "" { t.build.Status.Reason = build.BuildReasonPtr(build.TriggerNameCanNotBeBlank) - t.build.Status.Message = pointer.String("name is not set on when trigger condition") + t.build.Status.Message = ptr.To[string]("name is not set on when trigger condition") allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message)) } @@ -32,14 +32,14 @@ func (t *Trigger) validate(triggerWhen []build.TriggerWhen) []error { case build.GitHubWebHookTrigger: if when.GitHub == nil { t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidGitHubWebHook) - t.build.Status.Message = pointer.String(fmt.Sprintf( + t.build.Status.Message = ptr.To[string](fmt.Sprintf( "%q is missing required attribute `.github`", when.Name, )) allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message)) } else { if len(when.GitHub.Events) == 0 { t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidGitHubWebHook) - t.build.Status.Message = pointer.String(fmt.Sprintf( + t.build.Status.Message = ptr.To[string](fmt.Sprintf( "%q is missing required attribute `.github.events`", when.Name, )) allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message)) @@ -48,14 +48,14 @@ func (t *Trigger) validate(triggerWhen []build.TriggerWhen) []error { case build.ImageTrigger: if when.Image == nil { t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidImage) - t.build.Status.Message = pointer.String(fmt.Sprintf( + t.build.Status.Message = ptr.To[string](fmt.Sprintf( "%q is missing required attribute `.image`", when.Name, )) allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message)) } else { if len(when.Image.Names) == 0 { t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidImage) - t.build.Status.Message = pointer.String(fmt.Sprintf( + t.build.Status.Message = ptr.To[string](fmt.Sprintf( "%q is missing required attribute `.image.names`", when.Name, )) allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message)) @@ -64,21 +64,21 @@ func (t *Trigger) validate(triggerWhen []build.TriggerWhen) []error { case build.PipelineTrigger: if when.ObjectRef == nil { t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidPipeline) - t.build.Status.Message = pointer.String(fmt.Sprintf( + t.build.Status.Message = ptr.To[string](fmt.Sprintf( "%q is missing required attribute `.objectRef`", when.Name, )) allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message)) } else { if len(when.ObjectRef.Status) == 0 { t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidPipeline) - t.build.Status.Message = pointer.String(fmt.Sprintf( + t.build.Status.Message = ptr.To[string](fmt.Sprintf( "%q is missing required attribute `.objectRef.status`", when.Name, )) allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message)) } if when.ObjectRef.Name == "" && len(when.ObjectRef.Selector) == 0 { t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidPipeline) - t.build.Status.Message = pointer.String(fmt.Sprintf( + t.build.Status.Message = ptr.To[string](fmt.Sprintf( "%q is missing required attributes `.objectRef.name` or `.objectRef.selector`", when.Name, )) @@ -86,7 +86,7 @@ func (t *Trigger) validate(triggerWhen []build.TriggerWhen) []error { } if when.ObjectRef.Name != "" && len(when.ObjectRef.Selector) > 0 { t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidPipeline) - t.build.Status.Message = pointer.String(fmt.Sprintf( + t.build.Status.Message = ptr.To[string](fmt.Sprintf( "%q contains `.objectRef.name` and `.objectRef.selector`, must be only one", when.Name, )) @@ -95,7 +95,7 @@ func (t *Trigger) validate(triggerWhen []build.TriggerWhen) []error { } default: t.build.Status.Reason = build.BuildReasonPtr(build.TriggerInvalidType) - t.build.Status.Message = pointer.String( + t.build.Status.Message = ptr.To[string]( fmt.Sprintf("%q contains an invalid type %q", when.Name, when.Type)) allErrs = append(allErrs, fmt.Errorf("%s", *t.build.Status.Message)) } diff --git a/pkg/webhook/conversion/converter_test.go b/pkg/webhook/conversion/converter_test.go index f9d378038..6ecdc7e40 100644 --- a/pkg/webhook/conversion/converter_test.go +++ b/pkg/webhook/conversion/converter_test.go @@ -22,7 +22,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer/json" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) func getConversionReview(o string) (apiextensionsv1.ConversionReview, error) { @@ -497,7 +497,7 @@ request: }, }, Retention: &v1beta1.BuildRetention{ - AtBuildDeletion: pointer.Bool(true), + AtBuildDeletion: ptr.To[bool](true), }, Trigger: &v1beta1.Trigger{ When: []v1beta1.TriggerWhen{ @@ -1157,7 +1157,7 @@ request: Name: "dockerfile", Description: "The Dockerfile to be built.", Type: v1beta1.ParameterTypeString, - Default: pointer.String("Dockerfile"), + Default: ptr.To[string]("Dockerfile"), }, }, SecurityContext: &v1beta1.BuildStrategySecurityContext{ diff --git a/samples/build/build_buildah_shipwright_managed_push_cr.yaml b/samples/v1alpha1/build/build_buildah_shipwright_managed_push_cr.yaml similarity index 100% rename from samples/build/build_buildah_shipwright_managed_push_cr.yaml rename to samples/v1alpha1/build/build_buildah_shipwright_managed_push_cr.yaml diff --git a/samples/build/build_buildah_strategy_managed_push_cr.yaml b/samples/v1alpha1/build/build_buildah_strategy_managed_push_cr.yaml similarity index 100% rename from samples/build/build_buildah_strategy_managed_push_cr.yaml rename to samples/v1alpha1/build/build_buildah_strategy_managed_push_cr.yaml diff --git a/samples/build/build_buildkit_cr.yaml b/samples/v1alpha1/build/build_buildkit_cr.yaml similarity index 100% rename from samples/build/build_buildkit_cr.yaml rename to samples/v1alpha1/build/build_buildkit_cr.yaml diff --git a/samples/build/build_buildpacks-v3-heroku_cr.yaml b/samples/v1alpha1/build/build_buildpacks-v3-heroku_cr.yaml similarity index 100% rename from samples/build/build_buildpacks-v3-heroku_cr.yaml rename to samples/v1alpha1/build/build_buildpacks-v3-heroku_cr.yaml diff --git a/samples/build/build_buildpacks-v3-heroku_namespaced_cr.yaml b/samples/v1alpha1/build/build_buildpacks-v3-heroku_namespaced_cr.yaml similarity index 100% rename from samples/build/build_buildpacks-v3-heroku_namespaced_cr.yaml rename to samples/v1alpha1/build/build_buildpacks-v3-heroku_namespaced_cr.yaml diff --git a/samples/build/build_buildpacks-v3_cr.yaml b/samples/v1alpha1/build/build_buildpacks-v3_cr.yaml similarity index 100% rename from samples/build/build_buildpacks-v3_cr.yaml rename to samples/v1alpha1/build/build_buildpacks-v3_cr.yaml diff --git a/samples/build/build_buildpacks-v3_namespaced_cr.yaml b/samples/v1alpha1/build/build_buildpacks-v3_namespaced_cr.yaml similarity index 100% rename from samples/build/build_buildpacks-v3_namespaced_cr.yaml rename to samples/v1alpha1/build/build_buildpacks-v3_namespaced_cr.yaml diff --git a/samples/build/build_kaniko-trivy-bad_cr.yaml b/samples/v1alpha1/build/build_kaniko-trivy-bad_cr.yaml similarity index 100% rename from samples/build/build_kaniko-trivy-bad_cr.yaml rename to samples/v1alpha1/build/build_kaniko-trivy-bad_cr.yaml diff --git a/samples/build/build_kaniko-trivy-good_cr.yaml b/samples/v1alpha1/build/build_kaniko-trivy-good_cr.yaml similarity index 100% rename from samples/build/build_kaniko-trivy-good_cr.yaml rename to samples/v1alpha1/build/build_kaniko-trivy-good_cr.yaml diff --git a/samples/build/build_kaniko_cr.yaml b/samples/v1alpha1/build/build_kaniko_cr.yaml similarity index 100% rename from samples/build/build_kaniko_cr.yaml rename to samples/v1alpha1/build/build_kaniko_cr.yaml diff --git a/samples/build/build_ko_cr.yaml b/samples/v1alpha1/build/build_ko_cr.yaml similarity index 100% rename from samples/build/build_ko_cr.yaml rename to samples/v1alpha1/build/build_ko_cr.yaml diff --git a/samples/build/build_source-to-image_cr.yaml b/samples/v1alpha1/build/build_source-to-image_cr.yaml similarity index 100% rename from samples/build/build_source-to-image_cr.yaml rename to samples/v1alpha1/build/build_source-to-image_cr.yaml diff --git a/samples/buildrun/buildrun_buildah_cr.yaml b/samples/v1alpha1/buildrun/buildrun_buildah_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_buildah_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_buildah_cr.yaml diff --git a/samples/buildrun/buildrun_buildkit_cr.yaml b/samples/v1alpha1/buildrun/buildrun_buildkit_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_buildkit_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_buildkit_cr.yaml diff --git a/samples/buildrun/buildrun_buildpacks-v3-heroku_cr.yaml b/samples/v1alpha1/buildrun/buildrun_buildpacks-v3-heroku_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_buildpacks-v3-heroku_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_buildpacks-v3-heroku_cr.yaml diff --git a/samples/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml b/samples/v1alpha1/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml diff --git a/samples/buildrun/buildrun_buildpacks-v3_cr.yaml b/samples/v1alpha1/buildrun/buildrun_buildpacks-v3_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_buildpacks-v3_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_buildpacks-v3_cr.yaml diff --git a/samples/buildrun/buildrun_buildpacks-v3_namespaced_cr.yaml b/samples/v1alpha1/buildrun/buildrun_buildpacks-v3_namespaced_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_buildpacks-v3_namespaced_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_buildpacks-v3_namespaced_cr.yaml diff --git a/samples/buildrun/buildrun_kaniko-trivy-bad_cr.yaml b/samples/v1alpha1/buildrun/buildrun_kaniko-trivy-bad_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_kaniko-trivy-bad_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_kaniko-trivy-bad_cr.yaml diff --git a/samples/buildrun/buildrun_kaniko-trivy-good_cr.yaml b/samples/v1alpha1/buildrun/buildrun_kaniko-trivy-good_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_kaniko-trivy-good_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_kaniko-trivy-good_cr.yaml diff --git a/samples/buildrun/buildrun_kaniko_cr.yaml b/samples/v1alpha1/buildrun/buildrun_kaniko_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_kaniko_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_kaniko_cr.yaml diff --git a/samples/buildrun/buildrun_ko_cr.yaml b/samples/v1alpha1/buildrun/buildrun_ko_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_ko_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_ko_cr.yaml diff --git a/samples/buildrun/buildrun_source-to-image_cr.yaml b/samples/v1alpha1/buildrun/buildrun_source-to-image_cr.yaml similarity index 100% rename from samples/buildrun/buildrun_source-to-image_cr.yaml rename to samples/v1alpha1/buildrun/buildrun_source-to-image_cr.yaml diff --git a/samples/buildstrategy/buildah/buildstrategy_buildah_shipwright_managed_push_cr.yaml b/samples/v1alpha1/buildstrategy/buildah/buildstrategy_buildah_shipwright_managed_push_cr.yaml similarity index 100% rename from samples/buildstrategy/buildah/buildstrategy_buildah_shipwright_managed_push_cr.yaml rename to samples/v1alpha1/buildstrategy/buildah/buildstrategy_buildah_shipwright_managed_push_cr.yaml diff --git a/samples/buildstrategy/buildah/buildstrategy_buildah_strategy_managed_push_cr.yaml b/samples/v1alpha1/buildstrategy/buildah/buildstrategy_buildah_strategy_managed_push_cr.yaml similarity index 100% rename from samples/buildstrategy/buildah/buildstrategy_buildah_strategy_managed_push_cr.yaml rename to samples/v1alpha1/buildstrategy/buildah/buildstrategy_buildah_strategy_managed_push_cr.yaml diff --git a/samples/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml b/samples/v1alpha1/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml similarity index 100% rename from samples/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml rename to samples/v1alpha1/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml diff --git a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml b/samples/v1alpha1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml similarity index 100% rename from samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml rename to samples/v1alpha1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml diff --git a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml b/samples/v1alpha1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml similarity index 100% rename from samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml rename to samples/v1alpha1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml diff --git a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml b/samples/v1alpha1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml similarity index 100% rename from samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml rename to samples/v1alpha1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml diff --git a/samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml b/samples/v1alpha1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml similarity index 100% rename from samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml rename to samples/v1alpha1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml diff --git a/samples/v1alpha1/buildstrategy/buildstrategy/buildah/buildstrategy_buildah_shipwright_managed_push_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/buildah/buildstrategy_buildah_shipwright_managed_push_cr.yaml new file mode 100644 index 000000000..c95c2f6da --- /dev/null +++ b/samples/v1alpha1/buildstrategy/buildstrategy/buildah/buildstrategy_buildah_shipwright_managed_push_cr.yaml @@ -0,0 +1,204 @@ +--- +apiVersion: shipwright.io/v1alpha1 +kind: ClusterBuildStrategy +metadata: + name: buildah-shipwright-managed-push +spec: + buildSteps: + - name: build + image: quay.io/containers/buildah:v1.32.0 + workingDir: $(params.shp-source-root) + securityContext: + privileged: true + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Parse parameters + context= + dockerfile= + image= + target= + buildArgs=() + inBuildArgs=false + registriesBlock="" + inRegistriesBlock=false + registriesInsecure="" + inRegistriesInsecure=false + registriesSearch="" + inRegistriesSearch=false + while [[ $# -gt 0 ]]; do + arg="$1" + shift + + if [ "${arg}" == "--context" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + context="$1" + shift + elif [ "${arg}" == "--dockerfile" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + dockerfile="$1" + shift + elif [ "${arg}" == "--image" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + image="$1" + shift + elif [ "${arg}" == "--target" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + target="$1" + shift + elif [ "${arg}" == "--build-args" ]; then + inBuildArgs=true + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-block" ]; then + inRegistriesBlock=true + inBuildArgs=false + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-insecure" ]; then + inRegistriesInsecure=true + inBuildArgs=false + inRegistriesBlock=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-search" ]; then + inRegistriesSearch=true + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + elif [ "${inBuildArgs}" == "true" ]; then + buildArgs+=("--build-arg" "${arg}") + elif [ "${inRegistriesBlock}" == "true" ]; then + registriesBlock="${registriesBlock}'${arg}', " + elif [ "${inRegistriesInsecure}" == "true" ]; then + registriesInsecure="${registriesInsecure}'${arg}', " + elif [ "${inRegistriesSearch}" == "true" ]; then + registriesSearch="${registriesSearch}'${arg}', " + else + echo "Invalid usage" + exit 1 + fi + done + + # Verify the existence of the context directory + if [ ! -d "${context}" ]; then + echo -e "The context directory '${context}' does not exist." + echo -n "ContextDirNotFound" > '$(results.shp-error-reason.path)' + echo -n "The context directory '${context}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + cd "${context}" + + # Verify the existence of the Dockerfile + if [ ! -f "${dockerfile}" ]; then + echo -e "The Dockerfile '${dockerfile}' does not exist." + echo -n "DockerfileNotFound" > '$(results.shp-error-reason.path)' + echo -n "The Dockerfile '${dockerfile}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + echo "[INFO] Creating registries config file..." + if [ "${registriesSearch}" != "" ]; then + cat <>/tmp/registries.conf + [registries.search] + registries = [${registriesSearch::-2}] + + EOF + fi + if [ "${registriesInsecure}" != "" ]; then + cat <>/tmp/registries.conf + [registries.insecure] + registries = [${registriesInsecure::-2}] + + EOF + fi + if [ "${registriesBlock}" != "" ]; then + cat <>/tmp/registries.conf + [registries.block] + registries = [${registriesBlock::-2}] + + EOF + fi + + # Building the image + echo "[INFO] Building image ${image}" + buildah --storage-driver=$(params.storage-driver) \ + bud "${buildArgs[@]}" \ + --registries-conf=/tmp/registries.conf \ + --tag="${image}" \ + --file="${dockerfile}" \ + . + + # Write the image + echo "[INFO] Writing image ${image}" + buildah --storage-driver=$(params.storage-driver) push \ + "${image}" \ + "oci:${target}" + # That's the separator between the shell script and its args + - -- + - --context + - $(params.shp-source-context) + - --dockerfile + - $(build.dockerfile) + - --image + - $(params.shp-output-image) + - --build-args + - $(params.build-args[*]) + - --registries-block + - $(params.registries-block[*]) + - --registries-insecure + - $(params.registries-insecure[*]) + - --registries-search + - $(params.registries-search[*]) + - --target + - $(params.shp-output-directory) + resources: + limits: + cpu: "1" + memory: 2Gi + requests: + cpu: 250m + memory: 65Mi + parameters: + - name: build-args + description: "The values for the args in the Dockerfile. Values must be in the format KEY=VALUE." + type: array + defaults: [] + - name: registries-block + description: The registries that need to block pull access. + type: array + defaults: [] + - name: registries-insecure + description: The fully-qualified name of insecure registries. An insecure registry is one that does not have a valid SSL certificate or only supports HTTP. + type: array + defaults: [] + - name: registries-search + description: The registries for searching short name images such as `golang:latest`. + type: array + defaults: + - docker.io + - quay.io + - name: storage-driver + description: "The storage driver to use, such as 'overlay' or 'vfs'." + type: string + default: "vfs" + # For details see the "--storage-driver" section of https://github.com/containers/buildah/blob/main/docs/buildah.1.md#options + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1alpha1/buildstrategy/buildstrategy/buildah/buildstrategy_buildah_strategy_managed_push_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/buildah/buildstrategy_buildah_strategy_managed_push_cr.yaml new file mode 100644 index 000000000..03075863c --- /dev/null +++ b/samples/v1alpha1/buildstrategy/buildstrategy/buildah/buildstrategy_buildah_strategy_managed_push_cr.yaml @@ -0,0 +1,204 @@ +--- +apiVersion: shipwright.io/v1alpha1 +kind: ClusterBuildStrategy +metadata: + name: buildah-strategy-managed-push +spec: + buildSteps: + - name: build-and-push + image: quay.io/containers/buildah:v1.32.0 + workingDir: $(params.shp-source-root) + securityContext: + capabilities: + add: + - "SETFCAP" + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Parse parameters + context= + dockerfile= + image= + buildArgs=() + inBuildArgs=false + registriesBlock="" + inRegistriesBlock=false + registriesInsecure="" + inRegistriesInsecure=false + registriesSearch="" + inRegistriesSearch=false + tlsVerify=true + while [[ $# -gt 0 ]]; do + arg="$1" + shift + + if [ "${arg}" == "--context" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + context="$1" + shift + elif [ "${arg}" == "--dockerfile" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + dockerfile="$1" + shift + elif [ "${arg}" == "--image" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + image="$1" + shift + elif [ "${arg}" == "--build-args" ]; then + inBuildArgs=true + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-block" ]; then + inRegistriesBlock=true + inBuildArgs=false + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-insecure" ]; then + inRegistriesInsecure=true + inBuildArgs=false + inRegistriesBlock=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-search" ]; then + inRegistriesSearch=true + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + elif [ "${inBuildArgs}" == "true" ]; then + buildArgs+=("--build-arg" "${arg}") + elif [ "${inRegistriesBlock}" == "true" ]; then + registriesBlock="${registriesBlock}'${arg}', " + elif [ "${inRegistriesInsecure}" == "true" ]; then + registriesInsecure="${registriesInsecure}'${arg}', " + + # This assumes that the image is passed before the insecure registries which is fair in this context + if [[ ${image} == ${arg}/* ]]; then + tlsVerify=false + fi + elif [ "${inRegistriesSearch}" == "true" ]; then + registriesSearch="${registriesSearch}'${arg}', " + else + echo "Invalid usage" + exit 1 + fi + done + + # Verify the existence of the context directory + if [ ! -d "${context}" ]; then + echo -e "The context directory '${context}' does not exist." + echo -n "ContextDirNotFound" > '$(results.shp-error-reason.path)' + echo -n "The context directory '${context}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + cd "${context}" + + # Verify the existence of the Dockerfile + if [ ! -f "${dockerfile}" ]; then + echo -e "The Dockerfile '${dockerfile}' does not exist." + echo -n "DockerfileNotFound" > '$(results.shp-error-reason.path)' + echo -n "The Dockerfile '${dockerfile}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + echo "[INFO] Creating registries config file..." + if [ "${registriesSearch}" != "" ]; then + cat <>/tmp/registries.conf + [registries.search] + registries = [${registriesSearch::-2}] + + EOF + fi + if [ "${registriesInsecure}" != "" ]; then + cat <>/tmp/registries.conf + [registries.insecure] + registries = [${registriesInsecure::-2}] + + EOF + fi + if [ "${registriesBlock}" != "" ]; then + cat <>/tmp/registries.conf + [registries.block] + registries = [${registriesBlock::-2}] + + EOF + fi + + # Building the image + echo "[INFO] Building image ${image}" + buildah --storage-driver=$(params.storage-driver) \ + bud "${buildArgs[@]}" \ + --registries-conf=/tmp/registries.conf \ + --tag="${image}" \ + --file="${dockerfile}" \ + . + + # Push the image + echo "[INFO] Pushing image ${image}" + buildah --storage-driver=$(params.storage-driver) push \ + --digestfile='$(results.shp-image-digest.path)' \ + --tls-verify="${tlsVerify}" \ + "${image}" \ + "docker://${image}" + # That's the separator between the shell script and its args + - -- + - --context + - $(params.shp-source-context) + - --dockerfile + - $(build.dockerfile) + - --image + - $(params.shp-output-image) + - --build-args + - $(params.build-args[*]) + - --registries-block + - $(params.registries-block[*]) + - --registries-insecure + - $(params.registries-insecure[*]) + - --registries-search + - $(params.registries-search[*]) + resources: + limits: + cpu: "1" + memory: 2Gi + requests: + cpu: 250m + memory: 65Mi + parameters: + - name: build-args + description: "The values for the args in the Dockerfile. Values must be in the format KEY=VALUE." + type: array + defaults: [] + - name: registries-block + description: The registries that need to block pull access. + type: array + defaults: [] + - name: registries-insecure + description: The fully-qualified name of insecure registries. An insecure registry is one that does not have a valid SSL certificate or only supports HTTP. + type: array + defaults: [] + - name: registries-search + description: The registries for searching short name images such as `golang:latest`. + type: array + defaults: + - docker.io + - quay.io + - name: storage-driver + description: "The storage driver to use, such as 'overlay' or 'vfs'" + type: string + default: "vfs" + # For details see the "--storage-driver" section of https://github.com/containers/buildah/blob/main/docs/buildah.1.md#options + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1alpha1/buildstrategy/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml new file mode 100644 index 000000000..ff329d548 --- /dev/null +++ b/samples/v1alpha1/buildstrategy/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml @@ -0,0 +1,170 @@ +--- +apiVersion: shipwright.io/v1alpha1 +kind: ClusterBuildStrategy +metadata: + name: buildkit + annotations: + # See https://github.com/moby/buildkit/blob/master/docs/rootless.md#about---oci-worker-no-process-sandbox for more information + container.apparmor.security.beta.kubernetes.io/step-build-and-push: unconfined + # The usage of seccomp annotation will be deprecate in k8s v1.22.0, see + # https://kubernetes.io/docs/tutorials/clusters/seccomp/#create-a-pod-with-a-seccomp-profile-for-syscall-auditing for more information + container.seccomp.security.alpha.kubernetes.io/step-build-and-push: unconfined +spec: + parameters: + - name: build-args + description: "The values for the ARGs in the Dockerfile. Values must be in the format KEY=VALUE." + type: array + defaults: [] + - name: cache + description: "Configure BuildKit's cache usage. Allowed values are 'disabled' and 'registry'. The default is 'registry'." + type: string + default: registry + - name: platforms + description: "Build the image for different platforms. By default, the image is built for the platform used by the FROM image. If that is present for multiple platforms, then it is built for the environment's platform." + type: array + defaults: [] + - name: secrets + description: "The secrets to pass to the build. Values must be in the format ID=FILE_CONTENT." + type: array + defaults: [] + buildSteps: + - name: build-and-push + image: moby/buildkit:nightly-rootless + imagePullPolicy: Always + securityContext: + allowPrivilegeEscalation: true + capabilities: + add: + - SETGID + - SETUID + workingDir: $(params.shp-source-root) + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + # See https://github.com/moby/buildkit/blob/master/docs/rootless.md#about---oci-worker-no-process-sandbox for more information + - name: BUILDKITD_FLAGS + value: --oci-worker-no-process-sandbox + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_DOCKERFILE + value: $(params.DOCKERFILE) + - name: PARAM_OUTPUT_DIRECTORY + value: $(params.shp-output-directory) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + - name: PARAM_OUTPUT_INSECURE + value: $(params.shp-output-insecure) + - name: PARAM_CACHE + value: $(params.cache) + command: + - /bin/ash + args: + - -c + - | + set -euo pipefail + + # Verify the existence of the context directory + if [ ! -d "${PARAM_SOURCE_CONTEXT}" ]; then + echo -e "The context directory '${PARAM_SOURCE_CONTEXT}' does not exist." + echo -n "ContextDirNotFound" > '$(results.shp-error-reason.path)' + echo -n "The context directory '${PARAM_SOURCE_CONTEXT}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + # Prepare the file arguments + DOCKERFILE_PATH="${PARAM_SOURCE_CONTEXT}/${PARAM_DOCKERFILE}" + DOCKERFILE_DIR="$(dirname "${DOCKERFILE_PATH}")" + DOCKERFILE_NAME="$(basename "${DOCKERFILE_PATH}")" + + # Verify the existence of the Dockerfile + if [ ! -f "${DOCKERFILE_PATH}" ]; then + echo -e "The Dockerfile '${DOCKERFILE_PATH}' does not exist." + echo -n "DockerfileNotFound" > '$(results.shp-error-reason.path)' + echo -n "The Dockerfile '${DOCKERFILE_PATH}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + # We only have ash here and therefore no bash arrays to help add dynamic arguments (the build-args) to the build command. + + echo "#!/bin/ash" > /tmp/run.sh + echo "set -euo pipefail" >> /tmp/run.sh + echo "buildctl-daemonless.sh \\" >> /tmp/run.sh + echo "build \\" >> /tmp/run.sh + echo "--frontend=dockerfile.v0 \\" >> /tmp/run.sh + echo "--opt=filename=\"${DOCKERFILE_NAME}\" \\" >> /tmp/run.sh + echo "--local=context=\"${PARAM_SOURCE_CONTEXT}\" \\" >> /tmp/run.sh + echo "--local=dockerfile=\"${DOCKERFILE_DIR}\" \\" >> /tmp/run.sh + echo "--output=type=oci,tar=false,dest=\"${PARAM_OUTPUT_DIRECTORY}\" \\" >> /tmp/run.sh + if [ "${PARAM_CACHE}" == "registry" ]; then + echo "--export-cache=type=inline \\" >> /tmp/run.sh + echo "--import-cache=type=registry,ref=\"${PARAM_OUTPUT_IMAGE}\",registry.insecure=\"${PARAM_OUTPUT_INSECURE}\" \\" >> /tmp/run.sh + elif [ "${PARAM_CACHE}" == "disabled" ]; then + echo "--no-cache \\" >> /tmp/run.sh + else + echo -e "An invalid value for the parameter 'cache' has been provided: '${PARAM_CACHE}'. Allowed values are 'disabled' and 'registry'." + echo -n "InvalidParameterValue" > '$(results.shp-error-reason.path)' + echo -n "An invalid value for the parameter 'cache' has been provided: '${PARAM_CACHE}'. Allowed values are 'disabled' and 'registry'." > '$(results.shp-error-message.path)' + exit 1 + fi + + stage="" + platforms="" + for a in "$@" + do + if [ "${a}" == "--build-args" ]; then + stage=build-args + elif [ "${a}" == "--platforms" ]; then + stage=platforms + elif [ "${a}" == "--secrets" ]; then + stage=secrets + elif [ "${stage}" == "build-args" ]; then + echo "--opt=\"build-arg:${a}\" \\" >> /tmp/run.sh + elif [ "${stage}" == "platforms" ]; then + if [ "${platforms}" == "" ]; then + platforms="${a}" + else + platforms="${platforms},${a}" + fi + elif [ "${stage}" == "secrets" ]; then + # Split ID=FILE_CONTENT into variables id and data + + # using head because the data could be multiline + id="$(echo "${a}" | head -1 | sed 's/=.*//')" + + # This is hacky, we remove the suffix ${id}= from all lines of the data. + # If the data would be multiple lines and a line would start with ${id}= + # then we would remove it. We could force users to give us the secret + # base64 encoded. But ultimately, the best solution might be if the user + # mounts the secret and just gives us the path here. + data="$(echo "${a}" | sed "s/^${id}=//")" + + # Write the secret data into a temporary file, once we have volume support + # in the build strategy, we should use a memory based emptyDir for this. + echo -n "${data}" > "/tmp/secret_${id}" + + # Add the secret argument + echo "--secret id=${id},src="/tmp/secret_${id}" \\" >> /tmp/run.sh + fi + done + + if [ "${platforms}" != "" ]; then + echo "--opt=\"platform=${platforms}\" \\" >> /tmp/run.sh + fi + + echo "--progress=plain" >> /tmp/run.sh + + chmod +x /tmp/run.sh + /tmp/run.sh + # That's the separator between the shell script and its args + - -- + - --build-args + - $(params.build-args[*]) + - --platforms + - $(params.platforms[*]) + - --secrets + - $(params.secrets[*]) + securityContext: + runAsUser: 1000 + runAsGroup: 1000 diff --git a/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml new file mode 100644 index 000000000..61eca36ee --- /dev/null +++ b/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml @@ -0,0 +1,100 @@ +--- +apiVersion: shipwright.io/v1alpha1 +kind: ClusterBuildStrategy +metadata: + name: buildpacks-v3-heroku +spec: + volumes: + - name: platform-env + emptyDir: {} + parameters: + - name: platform-api-version + description: The referenced version is the minimum version that all relevant buildpack implementations support. + default: "0.7" + buildSteps: + - name: build-and-push + image: heroku/builder:22 + env: + - name: CNB_PLATFORM_API + value: $(params.platform-api-version) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + echo "> Processing environment variables..." + ENV_DIR="/platform/env" + + envs=($(env)) + + # Denying the creation of non required files from system environments. + # The creation of a file named PATH (corresponding to PATH system environment) + # caused failure for python source during pip install (https://github.com/Azure-Samples/python-docs-hello-world) + block_list=("PATH" "HOSTNAME" "PWD" "_" "SHLVL" "HOME" "") + + for env in "${envs[@]}"; do + blocked=false + + IFS='=' read -r key value string <<< "$env" + + for str in "${block_list[@]}"; do + if [[ "$key" == "$str" ]]; then + blocked=true + break + fi + done + + if [ "$blocked" == "false" ]; then + path="${ENV_DIR}/${key}" + echo -n "$value" > "$path" + fi + done + + LAYERS_DIR=/tmp/.shp/layers + CACHE_DIR=/tmp/.shp/cache + + mkdir -p "$CACHE_DIR" "$LAYERS_DIR" + + function announce_phase { + printf "===> %s\n" "$1" + } + + announce_phase "ANALYZING" + /cnb/lifecycle/analyzer -layers="$LAYERS_DIR" "${PARAM_OUTPUT_IMAGE}" + + announce_phase "DETECTING" + /cnb/lifecycle/detector -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + announce_phase "RESTORING" + /cnb/lifecycle/restorer -cache-dir="$CACHE_DIR" -layers="$LAYERS_DIR" + + announce_phase "BUILDING" + /cnb/lifecycle/builder -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + exporter_args=( -layers="$LAYERS_DIR" -report=/tmp/report.toml -cache-dir="$CACHE_DIR" -app="${PARAM_SOURCE_CONTEXT}") + grep -q "buildpack-default-process-type" "$LAYERS_DIR/config/metadata.toml" || exporter_args+=( -process-type web ) + + announce_phase "EXPORTING" + /cnb/lifecycle/exporter "${exporter_args[@]}" "${PARAM_OUTPUT_IMAGE}" + + # Store the image digest + grep digest /tmp/report.toml | tail -n 1 | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)" + volumeMounts: + - mountPath: /platform/env + name: platform-env + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 1000 + runAsGroup: 1000 diff --git a/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml new file mode 100644 index 000000000..aa0134055 --- /dev/null +++ b/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml @@ -0,0 +1,100 @@ +--- +apiVersion: shipwright.io/v1alpha1 +kind: BuildStrategy +metadata: + name: buildpacks-v3-heroku +spec: + volumes: + - name: platform-env + emptyDir: {} + parameters: + - name: platform-api-version + description: The referenced version is the minimum version that all relevant buildpack implementations support. + default: "0.7" + buildSteps: + - name: build-and-push + image: heroku/builder:22 + env: + - name: CNB_PLATFORM_API + value: $(params.platform-api-version) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + echo "> Processing environment variables..." + ENV_DIR="/platform/env" + + envs=($(env)) + + # Denying the creation of non required files from system environments. + # The creation of a file named PATH (corresponding to PATH system environment) + # caused failure for python source during pip install (https://github.com/Azure-Samples/python-docs-hello-world) + block_list=("PATH" "HOSTNAME" "PWD" "_" "SHLVL" "HOME" "") + + for env in "${envs[@]}"; do + blocked=false + + IFS='=' read -r key value string <<< "$env" + + for str in "${block_list[@]}"; do + if [[ "$key" == "$str" ]]; then + blocked=true + break + fi + done + + if [ "$blocked" == "false" ]; then + path="${ENV_DIR}/${key}" + echo -n "$value" > "$path" + fi + done + + LAYERS_DIR=/tmp/.shp/layers + CACHE_DIR=/tmp/.shp/cache + + mkdir -p "$CACHE_DIR" "$LAYERS_DIR" + + function announce_phase { + printf "===> %s\n" "$1" + } + + announce_phase "ANALYZING" + /cnb/lifecycle/analyzer -layers="$LAYERS_DIR" "${PARAM_OUTPUT_IMAGE}" + + announce_phase "DETECTING" + /cnb/lifecycle/detector -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + announce_phase "RESTORING" + /cnb/lifecycle/restorer -cache-dir="$CACHE_DIR" -layers="$LAYERS_DIR" + + announce_phase "BUILDING" + /cnb/lifecycle/builder -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + exporter_args=( -layers="$LAYERS_DIR" -report=/tmp/report.toml -cache-dir="$CACHE_DIR" -app="${PARAM_SOURCE_CONTEXT}") + grep -q "buildpack-default-process-type" "$LAYERS_DIR/config/metadata.toml" || exporter_args+=( -process-type web ) + + announce_phase "EXPORTING" + /cnb/lifecycle/exporter "${exporter_args[@]}" "${PARAM_OUTPUT_IMAGE}" + + # Store the image digest + grep digest /tmp/report.toml | tail -n 1 | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)" + volumeMounts: + - mountPath: /platform/env + name: platform-env + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 1000 + runAsGroup: 1000 diff --git a/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml new file mode 100644 index 000000000..f4c11a65f --- /dev/null +++ b/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml @@ -0,0 +1,100 @@ +--- +apiVersion: shipwright.io/v1alpha1 +kind: ClusterBuildStrategy +metadata: + name: buildpacks-v3 +spec: + volumes: + - name: platform-env + emptyDir: {} + parameters: + - name: platform-api-version + description: The referenced version is the minimum version that all relevant buildpack implementations support. + default: "0.7" + buildSteps: + - name: build-and-push + image: docker.io/paketobuildpacks/builder-jammy-full:latest + env: + - name: CNB_PLATFORM_API + value: $(params.platform-api-version) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + echo "> Processing environment variables..." + ENV_DIR="/platform/env" + + envs=($(env)) + + # Denying the creation of non required files from system environments. + # The creation of a file named PATH (corresponding to PATH system environment) + # caused failure for python source during pip install (https://github.com/Azure-Samples/python-docs-hello-world) + block_list=("PATH" "HOSTNAME" "PWD" "_" "SHLVL" "HOME" "") + + for env in "${envs[@]}"; do + blocked=false + + IFS='=' read -r key value string <<< "$env" + + for str in "${block_list[@]}"; do + if [[ "$key" == "$str" ]]; then + blocked=true + break + fi + done + + if [ "$blocked" == "false" ]; then + path="${ENV_DIR}/${key}" + echo -n "$value" > "$path" + fi + done + + LAYERS_DIR=/tmp/.shp/layers + CACHE_DIR=/tmp/.shp/cache + + mkdir -p "$CACHE_DIR" "$LAYERS_DIR" + + function announce_phase { + printf "===> %s\n" "$1" + } + + announce_phase "ANALYZING" + /cnb/lifecycle/analyzer -layers="$LAYERS_DIR" "${PARAM_OUTPUT_IMAGE}" + + announce_phase "DETECTING" + /cnb/lifecycle/detector -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + announce_phase "RESTORING" + /cnb/lifecycle/restorer -cache-dir="$CACHE_DIR" -layers="$LAYERS_DIR" + + announce_phase "BUILDING" + /cnb/lifecycle/builder -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + exporter_args=( -layers="$LAYERS_DIR" -report=/tmp/report.toml -cache-dir="$CACHE_DIR" -app="${PARAM_SOURCE_CONTEXT}") + grep -q "buildpack-default-process-type" "$LAYERS_DIR/config/metadata.toml" || exporter_args+=( -process-type web ) + + announce_phase "EXPORTING" + /cnb/lifecycle/exporter "${exporter_args[@]}" "${PARAM_OUTPUT_IMAGE}" + + # Store the image digest + grep digest /tmp/report.toml | tail -n 1 | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)" + volumeMounts: + - mountPath: /platform/env + name: platform-env + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 1001 + runAsGroup: 1000 diff --git a/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml new file mode 100644 index 000000000..32dcadc67 --- /dev/null +++ b/samples/v1alpha1/buildstrategy/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml @@ -0,0 +1,100 @@ +--- +apiVersion: shipwright.io/v1alpha1 +kind: BuildStrategy +metadata: + name: buildpacks-v3 +spec: + volumes: + - name: platform-env + emptyDir: {} + parameters: + - name: platform-api-version + description: The referenced version is the minimum version that all relevant buildpack implementations support. + default: "0.7" + buildSteps: + - name: build-and-push + image: docker.io/paketobuildpacks/builder-jammy-full:latest + env: + - name: CNB_PLATFORM_API + value: $(params.platform-api-version) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + echo "> Processing environment variables..." + ENV_DIR="/platform/env" + + envs=($(env)) + + # Denying the creation of non required files from system environments. + # The creation of a file named PATH (corresponding to PATH system environment) + # caused failure for python source during pip install (https://github.com/Azure-Samples/python-docs-hello-world) + block_list=("PATH" "HOSTNAME" "PWD" "_" "SHLVL" "HOME" "") + + for env in "${envs[@]}"; do + blocked=false + + IFS='=' read -r key value string <<< "$env" + + for str in "${block_list[@]}"; do + if [[ "$key" == "$str" ]]; then + blocked=true + break + fi + done + + if [ "$blocked" == "false" ]; then + path="${ENV_DIR}/${key}" + echo -n "$value" > "$path" + fi + done + + LAYERS_DIR=/tmp/.shp/layers + CACHE_DIR=/tmp/.shp/cache + + mkdir -p "$CACHE_DIR" "$LAYERS_DIR" + + function announce_phase { + printf "===> %s\n" "$1" + } + + announce_phase "ANALYZING" + /cnb/lifecycle/analyzer -layers="$LAYERS_DIR" "${PARAM_OUTPUT_IMAGE}" + + announce_phase "DETECTING" + /cnb/lifecycle/detector -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + announce_phase "RESTORING" + /cnb/lifecycle/restorer -cache-dir="$CACHE_DIR" -layers="$LAYERS_DIR" + + announce_phase "BUILDING" + /cnb/lifecycle/builder -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + exporter_args=( -layers="$LAYERS_DIR" -report=/tmp/report.toml -cache-dir="$CACHE_DIR" -app="${PARAM_SOURCE_CONTEXT}") + grep -q "buildpack-default-process-type" "$LAYERS_DIR/config/metadata.toml" || exporter_args+=( -process-type web ) + + announce_phase "EXPORTING" + /cnb/lifecycle/exporter "${exporter_args[@]}" "${PARAM_OUTPUT_IMAGE}" + + # Store the image digest + grep digest /tmp/report.toml | tail -n 1 | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)" + volumeMounts: + - mountPath: /platform/env + name: platform-env + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 1001 + runAsGroup: 1000 diff --git a/samples/buildstrategy/kaniko/buildstrategy_kaniko-trivy_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/kaniko/buildstrategy_kaniko-trivy_cr.yaml similarity index 100% rename from samples/buildstrategy/kaniko/buildstrategy_kaniko-trivy_cr.yaml rename to samples/v1alpha1/buildstrategy/buildstrategy/kaniko/buildstrategy_kaniko-trivy_cr.yaml diff --git a/samples/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml similarity index 100% rename from samples/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml rename to samples/v1alpha1/buildstrategy/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml diff --git a/samples/buildstrategy/ko/buildstrategy_ko_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/ko/buildstrategy_ko_cr.yaml similarity index 100% rename from samples/buildstrategy/ko/buildstrategy_ko_cr.yaml rename to samples/v1alpha1/buildstrategy/buildstrategy/ko/buildstrategy_ko_cr.yaml diff --git a/samples/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml similarity index 100% rename from samples/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml rename to samples/v1alpha1/buildstrategy/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml diff --git a/samples/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml b/samples/v1alpha1/buildstrategy/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml similarity index 100% rename from samples/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml rename to samples/v1alpha1/buildstrategy/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml diff --git a/samples/v1alpha1/buildstrategy/kaniko/buildstrategy_kaniko-trivy_cr.yaml b/samples/v1alpha1/buildstrategy/kaniko/buildstrategy_kaniko-trivy_cr.yaml new file mode 100644 index 000000000..eefbf8b25 --- /dev/null +++ b/samples/v1alpha1/buildstrategy/kaniko/buildstrategy_kaniko-trivy_cr.yaml @@ -0,0 +1,82 @@ +# This Build Strategy will intentionally fail if the image has any +# critical CVEs. It will not be pushed into the destination registry +# if any critical vulnerabilities are found. +--- +apiVersion: shipwright.io/v1alpha1 +kind: ClusterBuildStrategy +metadata: + name: kaniko-trivy +spec: + volumes: + - name: layout + emptyDir: {} + - name: tar + emptyDir: {} + buildSteps: + - name: kaniko-build + image: gcr.io/kaniko-project/executor:v1.16.0 + workingDir: $(params.shp-source-root) + securityContext: + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - SETFCAP + - KILL + env: + - name: HOME + value: /tekton/home + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + command: + - /kaniko/executor + args: + - --dockerfile + - $(build.dockerfile) + - --context + - $(params.shp-source-context) + - --destination + - $(params.shp-output-image) + - --snapshot-mode + - redo + - --no-push + - --tar-path + - $(params.shp-output-directory)/image.tar + # https://github.com/GoogleContainerTools/kaniko/issues/2164 + - --ignore-path + - /product_uuid + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + - name: trivy-scan + image: docker.io/aquasec/trivy:0.46.0 + command: + - trivy + args: + - image + - --exit-code=1 + - --severity=CRITICAL + - --input + - $(params.shp-output-directory)/image.tar + env: + - name: HOME + value: /tekton/home + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1alpha1/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml b/samples/v1alpha1/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml new file mode 100644 index 000000000..728375b0b --- /dev/null +++ b/samples/v1alpha1/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml @@ -0,0 +1,56 @@ +--- +apiVersion: shipwright.io/v1alpha1 +kind: ClusterBuildStrategy +metadata: + name: kaniko +spec: + buildSteps: + - name: build-and-push + image: gcr.io/kaniko-project/executor:v1.16.0 + workingDir: $(params.shp-source-root) + securityContext: + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - SETFCAP + - KILL + env: + - name: HOME + value: /tekton/home + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + command: + - /kaniko/executor + args: + - --dockerfile + - $(build.dockerfile) + - --context + - $(params.shp-source-context) + - --destination + - $(params.shp-output-image) + - --snapshot-mode + - redo + - --no-push + - --tar-path + - $(params.shp-output-directory)/image.tar + # https://github.com/GoogleContainerTools/kaniko/issues/2164 + - --ignore-path + - /product_uuid + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1alpha1/buildstrategy/ko/buildstrategy_ko_cr.yaml b/samples/v1alpha1/buildstrategy/ko/buildstrategy_ko_cr.yaml new file mode 100644 index 000000000..e98b0aede --- /dev/null +++ b/samples/v1alpha1/buildstrategy/ko/buildstrategy_ko_cr.yaml @@ -0,0 +1,116 @@ +--- +apiVersion: shipwright.io/v1alpha1 +kind: ClusterBuildStrategy +metadata: + name: ko +spec: + parameters: + - name: go-flags + description: "Value for the GOFLAGS environment variable." + default: "" + - name: go-version + description: "Version of Go, must match a tag from https://hub.docker.com/_/golang?tab=tags" + default: "1.20" + - name: ko-version + description: "Version of ko, must be either 'latest', or a release name from https://github.com/ko-build/ko/releases" + default: latest + - name: package-directory + description: "The directory inside the context directory containing the main package." + default: "." + - name: target-platform + description: "Target platform to be built. For example: 'linux/arm64'. Multiple platforms can be provided separated by comma, for example: 'linux/arm64,linux/amd64'. The value 'all' will build all platforms supported by the base image. The value 'current' will build the platform on which the build runs." + default: current + volumes: + - name: gocache + description: "Volume to contain the GOCACHE. Can be set to a persistent volume to optimize compilation performance for rebuilds." + overridable: true + emptyDir: {} + buildSteps: + - name: build + image: golang:$(params.go-version) + imagePullPolicy: Always + workingDir: $(params.shp-source-root) + volumeMounts: + - mountPath: /gocache + name: gocache + readOnly: false + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + - name: GOFLAGS + value: $(params.go-flags) + - name: GOCACHE + value: /gocache + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + - name: PARAM_OUTPUT_DIRECTORY + value: $(params.shp-output-directory) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_SOURCE_ROOT + value: $(params.shp-source-root) + - name: PARAM_TARGET_PLATFORM + value: $(params.target-platform) + - name: PARAM_PACKAGE_DIRECTORY + value: $(params.package-directory) + - name: PARAM_KO_VERSION + value: $(params.ko-version) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Determine the ko version + KO_VERSION="${PARAM_KO_VERSION}" + if [ "${KO_VERSION}" == "latest" ]; then + KO_VERSION=$(curl --silent "https://api.github.com/repos/ko-build/ko/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + fi + + # Create one variable with v-suffix and one without as we need both for the download URL + if [[ ${KO_VERSION} = v* ]]; then + KO_VERSION_WITH_V=${KO_VERSION} + KO_VERSION_WITHOUT_V=${KO_VERSION:1} + else + KO_VERSION_WITH_V=v${KO_VERSION} + KO_VERSION_WITHOUT_V=${KO_VERSION} + fi + + # Download ko to the temp directory + curl -f -s -L "https://github.com/ko-build/ko/releases/download/${KO_VERSION_WITH_V}/ko_${KO_VERSION_WITHOUT_V}_$(uname)_$(uname -m | sed 's/aarch64/arm64/').tar.gz" | tar xzf - -C /tmp ko + + # Determine the platform + PLATFORM="${PARAM_TARGET_PLATFORM}" + if [ "${PLATFORM}" == "current" ]; then + PLATFORM="$(uname | tr '[:upper:]' '[:lower:]')/$(uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/')" + fi + + # Print version information + go version + echo "ko version $(/tmp/ko version)" + + # Allow directory to be owned by other user which is normal for a volume-mounted directory. + # This allows Go to run git commands to access repository metadata. + # Documentation: https://git-scm.com/docs/git-config/2.39.0#Documentation/git-config.txt-safedirectory + git config --global --add safe.directory "${PARAM_SOURCE_ROOT}" + + # Run ko + + export GOROOT="$(go env GOROOT)" + + pushd "${PARAM_SOURCE_CONTEXT}" > /dev/null + /tmp/ko build "${PARAM_PACKAGE_DIRECTORY}" --oci-layout-path="${PARAM_OUTPUT_DIRECTORY}" --platform="${PLATFORM}" --push=false + popd > /dev/null + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 1000 + runAsGroup: 1000 diff --git a/samples/v1alpha1/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml b/samples/v1alpha1/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml new file mode 100644 index 000000000..09ac15037 --- /dev/null +++ b/samples/v1alpha1/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml @@ -0,0 +1,151 @@ +--- +apiVersion: shipwright.io/v1alpha1 +kind: ClusterBuildStrategy +metadata: + name: source-to-image-redhat +spec: + volumes: + - name: s2i + emptyDir: {} + buildSteps: + - name: s2i-generate + image: registry.redhat.io/ocp-tools-43-tech-preview/source-to-image-rhel8:latest + workingDir: $(params.shp-source-root) + args: + - build + - $(params.shp-source-context) + - $(build.builder.image) + - $(params.shp-output-image) + - --as-dockerfile=/s2i/Dockerfile + volumeMounts: + - name: s2i + mountPath: /s2i + - name: buildah + image: quay.io/containers/buildah:v1.32.0 + workingDir: /s2i + securityContext: + privileged: true + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Parse parameters + image= + target= + registriesBlock="" + inRegistriesBlock=false + registriesInsecure="" + inRegistriesInsecure=false + registriesSearch="" + inRegistriesSearch=false + while [[ $# -gt 0 ]]; do + arg="$1" + shift + + if [ "${arg}" == "--image" ]; then + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + image="$1" + shift + elif [ "${arg}" == "--target" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + target="$1" + shift + elif [ "${arg}" == "--registries-block" ]; then + inRegistriesBlock=true + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-insecure" ]; then + inRegistriesInsecure=true + inRegistriesBlock=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-search" ]; then + inRegistriesSearch=true + inRegistriesBlock=false + inRegistriesInsecure=false + elif [ "${inRegistriesBlock}" == "true" ]; then + registriesBlock="${registriesBlock}'${arg}', " + elif [ "${inRegistriesInsecure}" == "true" ]; then + registriesInsecure="${registriesInsecure}'${arg}', " + elif [ "${inRegistriesSearch}" == "true" ]; then + registriesSearch="${registriesSearch}'${arg}', " + else + echo "Invalid usage" + exit 1 + fi + done + + echo "[INFO] Creating registries config file..." + if [ "${registriesSearch}" != "" ]; then + cat <>/tmp/registries.conf + [registries.search] + registries = [${registriesSearch::-2}] + + EOF + fi + if [ "${registriesInsecure}" != "" ]; then + cat <>/tmp/registries.conf + [registries.insecure] + registries = [${registriesInsecure::-2}] + + EOF + fi + if [ "${registriesBlock}" != "" ]; then + cat <>/tmp/registries.conf + [registries.block] + registries = [${registriesBlock::-2}] + + EOF + fi + + # Building the image + echo "[INFO] Building image ${image}" + buildah bud \ + --registries-conf=/tmp/registries.conf \ + --tag="${image}" + + # Write the image + echo "[INFO] Writing image ${image}" + buildah push \ + "${image}" \ + "oci:${target}" + # That's the separator between the shell script and its args + - -- + - --image + - $(params.shp-output-image) + - --registries-block + - $(params.registries-block[*]) + - --registries-insecure + - $(params.registries-insecure[*]) + - --registries-search + - $(params.registries-search[*]) + - --target + - $(params.shp-output-directory) + volumeMounts: + - name: s2i + mountPath: /s2i + parameters: + - name: registries-block + description: The registries that need to block pull access. + type: array + defaults: [] + - name: registries-insecure + description: The fully-qualified name of insecure registries. An insecure registry is one that does not have a valid SSL certificate or only supports HTTP. + type: array + defaults: [] + - name: registries-search + description: The registries for searching short name images such as `golang:latest`. + type: array + defaults: + - docker.io + - quay.io + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1alpha1/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml b/samples/v1alpha1/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml new file mode 100644 index 000000000..ddadb030b --- /dev/null +++ b/samples/v1alpha1/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml @@ -0,0 +1,69 @@ +apiVersion: shipwright.io/v1alpha1 +kind: ClusterBuildStrategy +metadata: + name: source-to-image +spec: + volumes: + - name: gen-source + emptyDir: {} + buildSteps: + - command: + - /usr/local/bin/s2i + - build + - $(params.shp-source-context) + - $(build.builder.image) + - '--as-dockerfile' + - /gen-source/Dockerfile.gen + image: quay.io/openshift-pipeline/s2i:nightly + imagePullPolicy: Always + name: s2i-build-as-dockerfile + volumeMounts: + - mountPath: /gen-source + name: gen-source + workingDir: $(params.shp-source-root) + - name: build-and-push + image: gcr.io/kaniko-project/executor:v1.16.0 + command: + - /kaniko/executor + args: + - --dockerfile + - /gen-source/Dockerfile.gen + - --context + - /gen-source + - --destination + - $(params.shp-output-image) + - --snapshot-mode + - redo + - --no-push + - --tar-path + - $(params.shp-output-directory)/image.tar + # https://github.com/GoogleContainerTools/kaniko/issues/2164 + - --ignore-path + - /product_uuid + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - SETFCAP + - KILL + volumeMounts: + - mountPath: /gen-source + name: gen-source + workingDir: /gen-source + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1beta1/build/build_buildah_shipwright_managed_push_cr.yaml b/samples/v1beta1/build/build_buildah_shipwright_managed_push_cr.yaml new file mode 100644 index 000000000..cd7d04a6d --- /dev/null +++ b/samples/v1beta1/build/build_buildah_shipwright_managed_push_cr.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah-golang-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-go + contextDir: docker-build + strategy: + name: buildah-shipwright-managed-push + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/samples/v1beta1/build/build_buildah_strategy_managed_push_cr.yaml b/samples/v1beta1/build/build_buildah_strategy_managed_push_cr.yaml new file mode 100644 index 000000000..e16d934d3 --- /dev/null +++ b/samples/v1beta1/build/build_buildah_strategy_managed_push_cr.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah-golang-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-go + contextDir: docker-build + strategy: + name: buildah-strategy-managed-push + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/samples/v1beta1/build/build_buildkit_cr.yaml b/samples/v1beta1/build/build_buildkit_cr.yaml new file mode 100644 index 000000000..f6e435751 --- /dev/null +++ b/samples/v1beta1/build/build_buildkit_cr.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildkit-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-go + contextDir: docker-build + paramValues: + - name: platforms + values: + - value: linux/amd64 + - value: linux/arm64 + strategy: + name: buildkit + kind: ClusterBuildStrategy + retention: + atBuildDeletion: true + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app + diff --git a/samples/v1beta1/build/build_buildpacks-v3-heroku_cr.yaml b/samples/v1beta1/build/build_buildpacks-v3-heroku_cr.yaml new file mode 100644 index 000000000..eaaf1e5bf --- /dev/null +++ b/samples/v1beta1/build/build_buildpacks-v3-heroku_cr.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpack-nodejs-build-heroku +spec: + source: + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build-heroku + strategy: + name: buildpacks-v3-heroku + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/samples/v1beta1/build/build_buildpacks-v3-heroku_namespaced_cr.yaml b/samples/v1beta1/build/build_buildpacks-v3-heroku_namespaced_cr.yaml new file mode 100644 index 000000000..eb607cb70 --- /dev/null +++ b/samples/v1beta1/build/build_buildpacks-v3-heroku_namespaced_cr.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpack-nodejs-build-namespaced-heroku +spec: + source: + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build-heroku + strategy: + name: buildpacks-v3-heroku + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/samples/v1beta1/build/build_buildpacks-v3_cr.yaml b/samples/v1beta1/build/build_buildpacks-v3_cr.yaml new file mode 100644 index 000000000..da177dd24 --- /dev/null +++ b/samples/v1beta1/build/build_buildpacks-v3_cr.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpack-nodejs-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: ClusterBuildStrategy + retention: + atBuildDeletion: false + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/samples/v1beta1/build/build_buildpacks-v3_namespaced_cr.yaml b/samples/v1beta1/build/build_buildpacks-v3_namespaced_cr.yaml new file mode 100644 index 000000000..79ad64e34 --- /dev/null +++ b/samples/v1beta1/build/build_buildpacks-v3_namespaced_cr.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpack-nodejs-build-namespaced +spec: + source: + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/samples/v1beta1/build/build_kaniko-trivy-bad_cr.yaml b/samples/v1beta1/build/build_kaniko-trivy-bad_cr.yaml new file mode 100644 index 000000000..816a0ad52 --- /dev/null +++ b/samples/v1beta1/build/build_kaniko-trivy-bad_cr.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: kaniko-trivy-bad-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-scanning + strategy: + name: kaniko-trivy + kind: ClusterBuildStrategy + retention: + atBuildDeletion: true + paramValues: + - name: dockerfile + value: "Dockerfile.bad" + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/bad-trivy diff --git a/samples/v1beta1/build/build_kaniko-trivy-good_cr.yaml b/samples/v1beta1/build/build_kaniko-trivy-good_cr.yaml new file mode 100644 index 000000000..a27f5111e --- /dev/null +++ b/samples/v1beta1/build/build_kaniko-trivy-good_cr.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: kaniko-trivy-good-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-scanning + strategy: + name: kaniko-trivy + kind: ClusterBuildStrategy + retention: + atBuildDeletion: true + paramValues: + - name: dockerfile + value: "Dockerfile.good" + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/good-trivy diff --git a/samples/v1beta1/build/build_kaniko_cr.yaml b/samples/v1beta1/build/build_kaniko_cr.yaml new file mode 100644 index 000000000..2d56c5f3d --- /dev/null +++ b/samples/v1beta1/build/build_kaniko_cr.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: kaniko-golang-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-go + contextDir: docker-build + strategy: + name: kaniko + kind: ClusterBuildStrategy + retention: + atBuildDeletion: true + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/samples/v1beta1/build/build_ko_cr.yaml b/samples/v1beta1/build/build_ko_cr.yaml new file mode 100644 index 000000000..737f219cf --- /dev/null +++ b/samples/v1beta1/build/build_ko_cr.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: ko-build +spec: + paramValues: + - name: go-flags + value: "-v -mod=vendor -ldflags=-w" + - name: go-version + value: "1.20" + - name: package-directory + value: ./cmd/shipwright-build-controller + source: + git: + url: https://github.com/shipwright-io/build + strategy: + name: ko + kind: ClusterBuildStrategy + retention: + atBuildDeletion: false + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/shipwright-build diff --git a/samples/v1beta1/build/build_source-to-image_cr.yaml b/samples/v1beta1/build/build_source-to-image_cr.yaml new file mode 100644 index 000000000..ae9db48ef --- /dev/null +++ b/samples/v1beta1/build/build_source-to-image_cr.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: s2i-nodejs-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build/ + strategy: + name: source-to-image + kind: ClusterBuildStrategy + paramValues: + - name: builder-image + value: "docker.io/centos/nodejs-10-centos7" + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/nodejs-ex diff --git a/samples/v1beta1/buildrun/buildrun_buildah_cr.yaml b/samples/v1beta1/buildrun/buildrun_buildah_cr.yaml new file mode 100644 index 000000000..9100e6f0d --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_buildah_cr.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-golang-buildrun +spec: + build: + name: buildah-golang-build diff --git a/samples/v1beta1/buildrun/buildrun_buildkit_cr.yaml b/samples/v1beta1/buildrun/buildrun_buildkit_cr.yaml new file mode 100644 index 000000000..1a551f1c6 --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_buildkit_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildkit-buildrun +spec: + build: + name: buildkit-build + serviceAccount: ".generate" diff --git a/samples/v1beta1/buildrun/buildrun_buildpacks-v3-heroku_cr.yaml b/samples/v1beta1/buildrun/buildrun_buildpacks-v3-heroku_cr.yaml new file mode 100644 index 000000000..074f6dea5 --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_buildpacks-v3-heroku_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildpack-nodejs-buildrun-heroku +spec: + build: + name: buildpack-nodejs-build-heroku + serviceAccount: ".generate" diff --git a/samples/v1beta1/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml b/samples/v1beta1/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml new file mode 100644 index 000000000..9eab04226 --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildpack-nodejs-buildrun-namespaced-heroku +spec: + build: + name: buildpack-nodejs-build-namespaced-heroku + serviceAccount: ".generate" diff --git a/samples/v1beta1/buildrun/buildrun_buildpacks-v3_cr.yaml b/samples/v1beta1/buildrun/buildrun_buildpacks-v3_cr.yaml new file mode 100644 index 000000000..66f7babc2 --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_buildpacks-v3_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildpack-nodejs-buildrun +spec: + build: + name: buildpack-nodejs-build + serviceAccount: ".generate" diff --git a/samples/v1beta1/buildrun/buildrun_buildpacks-v3_namespaced_cr.yaml b/samples/v1beta1/buildrun/buildrun_buildpacks-v3_namespaced_cr.yaml new file mode 100644 index 000000000..14f0902d5 --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_buildpacks-v3_namespaced_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildpack-nodejs-buildrun-namespaced +spec: + build: + name: buildpack-nodejs-build-namespaced + serviceAccount: ".generate" diff --git a/samples/v1beta1/buildrun/buildrun_kaniko-trivy-bad_cr.yaml b/samples/v1beta1/buildrun/buildrun_kaniko-trivy-bad_cr.yaml new file mode 100644 index 000000000..89cd6a33e --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_kaniko-trivy-bad_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: kaniko-trivy-bad-buildrun +spec: + build: + name: kaniko-trivy-bad-build + serviceAccount: ".generate" diff --git a/samples/v1beta1/buildrun/buildrun_kaniko-trivy-good_cr.yaml b/samples/v1beta1/buildrun/buildrun_kaniko-trivy-good_cr.yaml new file mode 100644 index 000000000..08af9ad37 --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_kaniko-trivy-good_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: kaniko-trivy-good-buildrun +spec: + build: + name: kaniko-trivy-good-build + serviceAccount: ".generate" diff --git a/samples/v1beta1/buildrun/buildrun_kaniko_cr.yaml b/samples/v1beta1/buildrun/buildrun_kaniko_cr.yaml new file mode 100644 index 000000000..d47411b28 --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_kaniko_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: kaniko-golang-buildrun +spec: + build: + name: kaniko-golang-build + serviceAccount: ".generate" diff --git a/samples/v1beta1/buildrun/buildrun_ko_cr.yaml b/samples/v1beta1/buildrun/buildrun_ko_cr.yaml new file mode 100644 index 000000000..eab0b15f9 --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_ko_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: ko-buildrun +spec: + build: + name: ko-build + serviceAccount: ".generate" diff --git a/samples/v1beta1/buildrun/buildrun_source-to-image_cr.yaml b/samples/v1beta1/buildrun/buildrun_source-to-image_cr.yaml new file mode 100644 index 000000000..768ba6a60 --- /dev/null +++ b/samples/v1beta1/buildrun/buildrun_source-to-image_cr.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: s2i-nodejs-buildrun +spec: + build: + name: s2i-nodejs-build diff --git a/samples/v1beta1/buildstrategy/buildah/buildstrategy_buildah_shipwright_managed_push_cr.yaml b/samples/v1beta1/buildstrategy/buildah/buildstrategy_buildah_shipwright_managed_push_cr.yaml new file mode 100644 index 000000000..b40a35766 --- /dev/null +++ b/samples/v1beta1/buildstrategy/buildah/buildstrategy_buildah_shipwright_managed_push_cr.yaml @@ -0,0 +1,208 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: buildah-shipwright-managed-push +spec: + steps: + - name: build + image: quay.io/containers/buildah:v1.32.0 + workingDir: $(params.shp-source-root) + securityContext: + privileged: true + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Parse parameters + context= + dockerfile= + image= + target= + buildArgs=() + inBuildArgs=false + registriesBlock="" + inRegistriesBlock=false + registriesInsecure="" + inRegistriesInsecure=false + registriesSearch="" + inRegistriesSearch=false + while [[ $# -gt 0 ]]; do + arg="$1" + shift + + if [ "${arg}" == "--context" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + context="$1" + shift + elif [ "${arg}" == "--dockerfile" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + dockerfile="$1" + shift + elif [ "${arg}" == "--image" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + image="$1" + shift + elif [ "${arg}" == "--target" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + target="$1" + shift + elif [ "${arg}" == "--build-args" ]; then + inBuildArgs=true + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-block" ]; then + inRegistriesBlock=true + inBuildArgs=false + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-insecure" ]; then + inRegistriesInsecure=true + inBuildArgs=false + inRegistriesBlock=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-search" ]; then + inRegistriesSearch=true + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + elif [ "${inBuildArgs}" == "true" ]; then + buildArgs+=("--build-arg" "${arg}") + elif [ "${inRegistriesBlock}" == "true" ]; then + registriesBlock="${registriesBlock}'${arg}', " + elif [ "${inRegistriesInsecure}" == "true" ]; then + registriesInsecure="${registriesInsecure}'${arg}', " + elif [ "${inRegistriesSearch}" == "true" ]; then + registriesSearch="${registriesSearch}'${arg}', " + else + echo "Invalid usage" + exit 1 + fi + done + + # Verify the existence of the context directory + if [ ! -d "${context}" ]; then + echo -e "The context directory '${context}' does not exist." + echo -n "ContextDirNotFound" > '$(results.shp-error-reason.path)' + echo -n "The context directory '${context}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + cd "${context}" + + # Verify the existence of the Dockerfile + if [ ! -f "${dockerfile}" ]; then + echo -e "The Dockerfile '${dockerfile}' does not exist." + echo -n "DockerfileNotFound" > '$(results.shp-error-reason.path)' + echo -n "The Dockerfile '${dockerfile}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + echo "[INFO] Creating registries config file..." + if [ "${registriesSearch}" != "" ]; then + cat <>/tmp/registries.conf + [registries.search] + registries = [${registriesSearch::-2}] + + EOF + fi + if [ "${registriesInsecure}" != "" ]; then + cat <>/tmp/registries.conf + [registries.insecure] + registries = [${registriesInsecure::-2}] + + EOF + fi + if [ "${registriesBlock}" != "" ]; then + cat <>/tmp/registries.conf + [registries.block] + registries = [${registriesBlock::-2}] + + EOF + fi + + # Building the image + echo "[INFO] Building image ${image}" + buildah --storage-driver=$(params.storage-driver) \ + bud "${buildArgs[@]}" \ + --registries-conf=/tmp/registries.conf \ + --tag="${image}" \ + --file="${dockerfile}" \ + . + + # Write the image + echo "[INFO] Writing image ${image}" + buildah --storage-driver=$(params.storage-driver) push \ + "${image}" \ + "oci:${target}" + # That's the separator between the shell script and its args + - -- + - --context + - $(params.shp-source-context) + - --dockerfile + - $(params.dockerfile) + - --image + - $(params.shp-output-image) + - --build-args + - $(params.build-args[*]) + - --registries-block + - $(params.registries-block[*]) + - --registries-insecure + - $(params.registries-insecure[*]) + - --registries-search + - $(params.registries-search[*]) + - --target + - $(params.shp-output-directory) + resources: + limits: + cpu: "1" + memory: 2Gi + requests: + cpu: 250m + memory: 65Mi + parameters: + - name: build-args + description: "The values for the args in the Dockerfile. Values must be in the format KEY=VALUE." + type: array + defaults: [] + - name: registries-block + description: The registries that need to block pull access. + type: array + defaults: [] + - name: registries-insecure + description: The fully-qualified name of insecure registries. An insecure registry is one that does not have a valid SSL certificate or only supports HTTP. + type: array + defaults: [] + - name: registries-search + description: The registries for searching short name images such as `golang:latest`. + type: array + defaults: + - docker.io + - quay.io + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: "Dockerfile" + - name: storage-driver + description: "The storage driver to use, such as 'overlay' or 'vfs'." + type: string + default: "vfs" + # For details see the "--storage-driver" section of https://github.com/containers/buildah/blob/main/docs/buildah.1.md#options + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1beta1/buildstrategy/buildah/buildstrategy_buildah_strategy_managed_push_cr.yaml b/samples/v1beta1/buildstrategy/buildah/buildstrategy_buildah_strategy_managed_push_cr.yaml new file mode 100644 index 000000000..4434a6f40 --- /dev/null +++ b/samples/v1beta1/buildstrategy/buildah/buildstrategy_buildah_strategy_managed_push_cr.yaml @@ -0,0 +1,208 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: buildah-strategy-managed-push +spec: + steps: + - name: build-and-push + image: quay.io/containers/buildah:v1.32.0 + workingDir: $(params.shp-source-root) + securityContext: + capabilities: + add: + - "SETFCAP" + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Parse parameters + context= + dockerfile= + image= + buildArgs=() + inBuildArgs=false + registriesBlock="" + inRegistriesBlock=false + registriesInsecure="" + inRegistriesInsecure=false + registriesSearch="" + inRegistriesSearch=false + tlsVerify=true + while [[ $# -gt 0 ]]; do + arg="$1" + shift + + if [ "${arg}" == "--context" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + context="$1" + shift + elif [ "${arg}" == "--dockerfile" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + dockerfile="$1" + shift + elif [ "${arg}" == "--image" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + image="$1" + shift + elif [ "${arg}" == "--build-args" ]; then + inBuildArgs=true + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-block" ]; then + inRegistriesBlock=true + inBuildArgs=false + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-insecure" ]; then + inRegistriesInsecure=true + inBuildArgs=false + inRegistriesBlock=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-search" ]; then + inRegistriesSearch=true + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + elif [ "${inBuildArgs}" == "true" ]; then + buildArgs+=("--build-arg" "${arg}") + elif [ "${inRegistriesBlock}" == "true" ]; then + registriesBlock="${registriesBlock}'${arg}', " + elif [ "${inRegistriesInsecure}" == "true" ]; then + registriesInsecure="${registriesInsecure}'${arg}', " + + # This assumes that the image is passed before the insecure registries which is fair in this context + if [[ ${image} == ${arg}/* ]]; then + tlsVerify=false + fi + elif [ "${inRegistriesSearch}" == "true" ]; then + registriesSearch="${registriesSearch}'${arg}', " + else + echo "Invalid usage" + exit 1 + fi + done + + # Verify the existence of the context directory + if [ ! -d "${context}" ]; then + echo -e "The context directory '${context}' does not exist." + echo -n "ContextDirNotFound" > '$(results.shp-error-reason.path)' + echo -n "The context directory '${context}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + cd "${context}" + + # Verify the existence of the Dockerfile + if [ ! -f "${dockerfile}" ]; then + echo -e "The Dockerfile '${dockerfile}' does not exist." + echo -n "DockerfileNotFound" > '$(results.shp-error-reason.path)' + echo -n "The Dockerfile '${dockerfile}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + echo "[INFO] Creating registries config file..." + if [ "${registriesSearch}" != "" ]; then + cat <>/tmp/registries.conf + [registries.search] + registries = [${registriesSearch::-2}] + + EOF + fi + if [ "${registriesInsecure}" != "" ]; then + cat <>/tmp/registries.conf + [registries.insecure] + registries = [${registriesInsecure::-2}] + + EOF + fi + if [ "${registriesBlock}" != "" ]; then + cat <>/tmp/registries.conf + [registries.block] + registries = [${registriesBlock::-2}] + + EOF + fi + + # Building the image + echo "[INFO] Building image ${image}" + buildah --storage-driver=$(params.storage-driver) \ + bud "${buildArgs[@]}" \ + --registries-conf=/tmp/registries.conf \ + --tag="${image}" \ + --file="${dockerfile}" \ + . + + # Push the image + echo "[INFO] Pushing image ${image}" + buildah --storage-driver=$(params.storage-driver) push \ + --digestfile='$(results.shp-image-digest.path)' \ + --tls-verify="${tlsVerify}" \ + "${image}" \ + "docker://${image}" + # That's the separator between the shell script and its args + - -- + - --context + - $(params.shp-source-context) + - --dockerfile + - $(params.dockerfile) + - --image + - $(params.shp-output-image) + - --build-args + - $(params.build-args[*]) + - --registries-block + - $(params.registries-block[*]) + - --registries-insecure + - $(params.registries-insecure[*]) + - --registries-search + - $(params.registries-search[*]) + resources: + limits: + cpu: "1" + memory: 2Gi + requests: + cpu: 250m + memory: 65Mi + parameters: + - name: build-args + description: "The values for the args in the Dockerfile. Values must be in the format KEY=VALUE." + type: array + defaults: [] + - name: registries-block + description: The registries that need to block pull access. + type: array + defaults: [] + - name: registries-insecure + description: The fully-qualified name of insecure registries. An insecure registry is one that does not have a valid SSL certificate or only supports HTTP. + type: array + defaults: [] + - name: registries-search + description: The registries for searching short name images such as `golang:latest`. + type: array + defaults: + - docker.io + - quay.io + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: "Dockerfile" + - name: storage-driver + description: "The storage driver to use, such as 'overlay' or 'vfs'" + type: string + default: "vfs" + # For details see the "--storage-driver" section of https://github.com/containers/buildah/blob/main/docs/buildah.1.md#options + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1beta1/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml b/samples/v1beta1/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml new file mode 100644 index 000000000..2469cf3e8 --- /dev/null +++ b/samples/v1beta1/buildstrategy/buildkit/buildstrategy_buildkit_cr.yaml @@ -0,0 +1,174 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: buildkit + annotations: + # See https://github.com/moby/buildkit/blob/master/docs/rootless.md#about---oci-worker-no-process-sandbox for more information + container.apparmor.security.beta.kubernetes.io/step-build-and-push: unconfined + # The usage of seccomp annotation will be deprecate in k8s v1.22.0, see + # https://kubernetes.io/docs/tutorials/clusters/seccomp/#create-a-pod-with-a-seccomp-profile-for-syscall-auditing for more information + container.seccomp.security.alpha.kubernetes.io/step-build-and-push: unconfined +spec: + parameters: + - name: build-args + description: "The values for the ARGs in the Dockerfile. Values must be in the format KEY=VALUE." + type: array + defaults: [] + - name: cache + description: "Configure BuildKit's cache usage. Allowed values are 'disabled' and 'registry'. The default is 'registry'." + type: string + default: registry + - name: platforms + description: "Build the image for different platforms. By default, the image is built for the platform used by the FROM image. If that is present for multiple platforms, then it is built for the environment's platform." + type: array + defaults: [] + - name: secrets + description: "The secrets to pass to the build. Values must be in the format ID=FILE_CONTENT." + type: array + defaults: [] + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: "Dockerfile" + steps: + - name: build-and-push + image: moby/buildkit:nightly-rootless + imagePullPolicy: Always + securityContext: + allowPrivilegeEscalation: true + capabilities: + add: + - SETGID + - SETUID + workingDir: $(params.shp-source-root) + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + # See https://github.com/moby/buildkit/blob/master/docs/rootless.md#about---oci-worker-no-process-sandbox for more information + - name: BUILDKITD_FLAGS + value: --oci-worker-no-process-sandbox + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_DOCKERFILE + value: $(params.dockerfile) + - name: PARAM_OUTPUT_DIRECTORY + value: $(params.shp-output-directory) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + - name: PARAM_OUTPUT_INSECURE + value: $(params.shp-output-insecure) + - name: PARAM_CACHE + value: $(params.cache) + command: + - /bin/ash + args: + - -c + - | + set -euo pipefail + + # Verify the existence of the context directory + if [ ! -d "${PARAM_SOURCE_CONTEXT}" ]; then + echo -e "The context directory '${PARAM_SOURCE_CONTEXT}' does not exist." + echo -n "ContextDirNotFound" > '$(results.shp-error-reason.path)' + echo -n "The context directory '${PARAM_SOURCE_CONTEXT}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + # Prepare the file arguments + DOCKERFILE_PATH="${PARAM_SOURCE_CONTEXT}/${PARAM_DOCKERFILE}" + DOCKERFILE_DIR="$(dirname "${DOCKERFILE_PATH}")" + DOCKERFILE_NAME="$(basename "${DOCKERFILE_PATH}")" + + # Verify the existence of the Dockerfile + if [ ! -f "${DOCKERFILE_PATH}" ]; then + echo -e "The Dockerfile '${DOCKERFILE_PATH}' does not exist." + echo -n "DockerfileNotFound" > '$(results.shp-error-reason.path)' + echo -n "The Dockerfile '${DOCKERFILE_PATH}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + # We only have ash here and therefore no bash arrays to help add dynamic arguments (the build-args) to the build command. + + echo "#!/bin/ash" > /tmp/run.sh + echo "set -euo pipefail" >> /tmp/run.sh + echo "buildctl-daemonless.sh \\" >> /tmp/run.sh + echo "build \\" >> /tmp/run.sh + echo "--frontend=dockerfile.v0 \\" >> /tmp/run.sh + echo "--opt=filename=\"${DOCKERFILE_NAME}\" \\" >> /tmp/run.sh + echo "--local=context=\"${PARAM_SOURCE_CONTEXT}\" \\" >> /tmp/run.sh + echo "--local=dockerfile=\"${DOCKERFILE_DIR}\" \\" >> /tmp/run.sh + echo "--output=type=oci,tar=false,dest=\"${PARAM_OUTPUT_DIRECTORY}\" \\" >> /tmp/run.sh + if [ "${PARAM_CACHE}" == "registry" ]; then + echo "--export-cache=type=inline \\" >> /tmp/run.sh + echo "--import-cache=type=registry,ref=\"${PARAM_OUTPUT_IMAGE}\",registry.insecure=\"${PARAM_OUTPUT_INSECURE}\" \\" >> /tmp/run.sh + elif [ "${PARAM_CACHE}" == "disabled" ]; then + echo "--no-cache \\" >> /tmp/run.sh + else + echo -e "An invalid value for the parameter 'cache' has been provided: '${PARAM_CACHE}'. Allowed values are 'disabled' and 'registry'." + echo -n "InvalidParameterValue" > '$(results.shp-error-reason.path)' + echo -n "An invalid value for the parameter 'cache' has been provided: '${PARAM_CACHE}'. Allowed values are 'disabled' and 'registry'." > '$(results.shp-error-message.path)' + exit 1 + fi + + stage="" + platforms="" + for a in "$@" + do + if [ "${a}" == "--build-args" ]; then + stage=build-args + elif [ "${a}" == "--platforms" ]; then + stage=platforms + elif [ "${a}" == "--secrets" ]; then + stage=secrets + elif [ "${stage}" == "build-args" ]; then + echo "--opt=\"build-arg:${a}\" \\" >> /tmp/run.sh + elif [ "${stage}" == "platforms" ]; then + if [ "${platforms}" == "" ]; then + platforms="${a}" + else + platforms="${platforms},${a}" + fi + elif [ "${stage}" == "secrets" ]; then + # Split ID=FILE_CONTENT into variables id and data + + # using head because the data could be multiline + id="$(echo "${a}" | head -1 | sed 's/=.*//')" + + # This is hacky, we remove the suffix ${id}= from all lines of the data. + # If the data would be multiple lines and a line would start with ${id}= + # then we would remove it. We could force users to give us the secret + # base64 encoded. But ultimately, the best solution might be if the user + # mounts the secret and just gives us the path here. + data="$(echo "${a}" | sed "s/^${id}=//")" + + # Write the secret data into a temporary file, once we have volume support + # in the build strategy, we should use a memory based emptyDir for this. + echo -n "${data}" > "/tmp/secret_${id}" + + # Add the secret argument + echo "--secret id=${id},src="/tmp/secret_${id}" \\" >> /tmp/run.sh + fi + done + + if [ "${platforms}" != "" ]; then + echo "--opt=\"platform=${platforms}\" \\" >> /tmp/run.sh + fi + + echo "--progress=plain" >> /tmp/run.sh + + chmod +x /tmp/run.sh + /tmp/run.sh + # That's the separator between the shell script and its args + - -- + - --build-args + - $(params.build-args[*]) + - --platforms + - $(params.platforms[*]) + - --secrets + - $(params.secrets[*]) + securityContext: + runAsUser: 1000 + runAsGroup: 1000 diff --git a/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml b/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml new file mode 100644 index 000000000..11494bb45 --- /dev/null +++ b/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_cr.yaml @@ -0,0 +1,100 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: buildpacks-v3-heroku +spec: + volumes: + - name: platform-env + emptyDir: {} + parameters: + - name: platform-api-version + description: The referenced version is the minimum version that all relevant buildpack implementations support. + default: "0.7" + steps: + - name: build-and-push + image: heroku/builder:22 + env: + - name: CNB_PLATFORM_API + value: $(params.platform-api-version) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + echo "> Processing environment variables..." + ENV_DIR="/platform/env" + + envs=($(env)) + + # Denying the creation of non required files from system environments. + # The creation of a file named PATH (corresponding to PATH system environment) + # caused failure for python source during pip install (https://github.com/Azure-Samples/python-docs-hello-world) + block_list=("PATH" "HOSTNAME" "PWD" "_" "SHLVL" "HOME" "") + + for env in "${envs[@]}"; do + blocked=false + + IFS='=' read -r key value string <<< "$env" + + for str in "${block_list[@]}"; do + if [[ "$key" == "$str" ]]; then + blocked=true + break + fi + done + + if [ "$blocked" == "false" ]; then + path="${ENV_DIR}/${key}" + echo -n "$value" > "$path" + fi + done + + LAYERS_DIR=/tmp/.shp/layers + CACHE_DIR=/tmp/.shp/cache + + mkdir -p "$CACHE_DIR" "$LAYERS_DIR" + + function announce_phase { + printf "===> %s\n" "$1" + } + + announce_phase "ANALYZING" + /cnb/lifecycle/analyzer -layers="$LAYERS_DIR" "${PARAM_OUTPUT_IMAGE}" + + announce_phase "DETECTING" + /cnb/lifecycle/detector -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + announce_phase "RESTORING" + /cnb/lifecycle/restorer -cache-dir="$CACHE_DIR" -layers="$LAYERS_DIR" + + announce_phase "BUILDING" + /cnb/lifecycle/builder -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + exporter_args=( -layers="$LAYERS_DIR" -report=/tmp/report.toml -cache-dir="$CACHE_DIR" -app="${PARAM_SOURCE_CONTEXT}") + grep -q "buildpack-default-process-type" "$LAYERS_DIR/config/metadata.toml" || exporter_args+=( -process-type web ) + + announce_phase "EXPORTING" + /cnb/lifecycle/exporter "${exporter_args[@]}" "${PARAM_OUTPUT_IMAGE}" + + # Store the image digest + grep digest /tmp/report.toml | tail -n 1 | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)" + volumeMounts: + - mountPath: /platform/env + name: platform-env + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 1000 + runAsGroup: 1000 diff --git a/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml b/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml new file mode 100644 index 000000000..c960cdd54 --- /dev/null +++ b/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml @@ -0,0 +1,100 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: buildpacks-v3-heroku +spec: + volumes: + - name: platform-env + emptyDir: {} + parameters: + - name: platform-api-version + description: The referenced version is the minimum version that all relevant buildpack implementations support. + default: "0.7" + steps: + - name: build-and-push + image: heroku/builder:22 + env: + - name: CNB_PLATFORM_API + value: $(params.platform-api-version) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + echo "> Processing environment variables..." + ENV_DIR="/platform/env" + + envs=($(env)) + + # Denying the creation of non required files from system environments. + # The creation of a file named PATH (corresponding to PATH system environment) + # caused failure for python source during pip install (https://github.com/Azure-Samples/python-docs-hello-world) + block_list=("PATH" "HOSTNAME" "PWD" "_" "SHLVL" "HOME" "") + + for env in "${envs[@]}"; do + blocked=false + + IFS='=' read -r key value string <<< "$env" + + for str in "${block_list[@]}"; do + if [[ "$key" == "$str" ]]; then + blocked=true + break + fi + done + + if [ "$blocked" == "false" ]; then + path="${ENV_DIR}/${key}" + echo -n "$value" > "$path" + fi + done + + LAYERS_DIR=/tmp/.shp/layers + CACHE_DIR=/tmp/.shp/cache + + mkdir -p "$CACHE_DIR" "$LAYERS_DIR" + + function announce_phase { + printf "===> %s\n" "$1" + } + + announce_phase "ANALYZING" + /cnb/lifecycle/analyzer -layers="$LAYERS_DIR" "${PARAM_OUTPUT_IMAGE}" + + announce_phase "DETECTING" + /cnb/lifecycle/detector -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + announce_phase "RESTORING" + /cnb/lifecycle/restorer -cache-dir="$CACHE_DIR" -layers="$LAYERS_DIR" + + announce_phase "BUILDING" + /cnb/lifecycle/builder -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + exporter_args=( -layers="$LAYERS_DIR" -report=/tmp/report.toml -cache-dir="$CACHE_DIR" -app="${PARAM_SOURCE_CONTEXT}") + grep -q "buildpack-default-process-type" "$LAYERS_DIR/config/metadata.toml" || exporter_args+=( -process-type web ) + + announce_phase "EXPORTING" + /cnb/lifecycle/exporter "${exporter_args[@]}" "${PARAM_OUTPUT_IMAGE}" + + # Store the image digest + grep digest /tmp/report.toml | tail -n 1 | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)" + volumeMounts: + - mountPath: /platform/env + name: platform-env + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 1000 + runAsGroup: 1000 diff --git a/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml b/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml new file mode 100644 index 000000000..678a07017 --- /dev/null +++ b/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_cr.yaml @@ -0,0 +1,100 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: buildpacks-v3 +spec: + volumes: + - name: platform-env + emptyDir: {} + parameters: + - name: platform-api-version + description: The referenced version is the minimum version that all relevant buildpack implementations support. + default: "0.7" + steps: + - name: build-and-push + image: docker.io/paketobuildpacks/builder-jammy-full:latest + env: + - name: CNB_PLATFORM_API + value: $(params.platform-api-version) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + echo "> Processing environment variables..." + ENV_DIR="/platform/env" + + envs=($(env)) + + # Denying the creation of non required files from system environments. + # The creation of a file named PATH (corresponding to PATH system environment) + # caused failure for python source during pip install (https://github.com/Azure-Samples/python-docs-hello-world) + block_list=("PATH" "HOSTNAME" "PWD" "_" "SHLVL" "HOME" "") + + for env in "${envs[@]}"; do + blocked=false + + IFS='=' read -r key value string <<< "$env" + + for str in "${block_list[@]}"; do + if [[ "$key" == "$str" ]]; then + blocked=true + break + fi + done + + if [ "$blocked" == "false" ]; then + path="${ENV_DIR}/${key}" + echo -n "$value" > "$path" + fi + done + + LAYERS_DIR=/tmp/.shp/layers + CACHE_DIR=/tmp/.shp/cache + + mkdir -p "$CACHE_DIR" "$LAYERS_DIR" + + function announce_phase { + printf "===> %s\n" "$1" + } + + announce_phase "ANALYZING" + /cnb/lifecycle/analyzer -layers="$LAYERS_DIR" "${PARAM_OUTPUT_IMAGE}" + + announce_phase "DETECTING" + /cnb/lifecycle/detector -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + announce_phase "RESTORING" + /cnb/lifecycle/restorer -cache-dir="$CACHE_DIR" -layers="$LAYERS_DIR" + + announce_phase "BUILDING" + /cnb/lifecycle/builder -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + exporter_args=( -layers="$LAYERS_DIR" -report=/tmp/report.toml -cache-dir="$CACHE_DIR" -app="${PARAM_SOURCE_CONTEXT}") + grep -q "buildpack-default-process-type" "$LAYERS_DIR/config/metadata.toml" || exporter_args+=( -process-type web ) + + announce_phase "EXPORTING" + /cnb/lifecycle/exporter "${exporter_args[@]}" "${PARAM_OUTPUT_IMAGE}" + + # Store the image digest + grep digest /tmp/report.toml | tail -n 1 | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)" + volumeMounts: + - mountPath: /platform/env + name: platform-env + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 1001 + runAsGroup: 1000 diff --git a/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml b/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml new file mode 100644 index 000000000..034a17bf9 --- /dev/null +++ b/samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml @@ -0,0 +1,100 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: buildpacks-v3 +spec: + volumes: + - name: platform-env + emptyDir: {} + parameters: + - name: platform-api-version + description: The referenced version is the minimum version that all relevant buildpack implementations support. + default: "0.7" + steps: + - name: build-and-push + image: docker.io/paketobuildpacks/builder-jammy-full:latest + env: + - name: CNB_PLATFORM_API + value: $(params.platform-api-version) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + echo "> Processing environment variables..." + ENV_DIR="/platform/env" + + envs=($(env)) + + # Denying the creation of non required files from system environments. + # The creation of a file named PATH (corresponding to PATH system environment) + # caused failure for python source during pip install (https://github.com/Azure-Samples/python-docs-hello-world) + block_list=("PATH" "HOSTNAME" "PWD" "_" "SHLVL" "HOME" "") + + for env in "${envs[@]}"; do + blocked=false + + IFS='=' read -r key value string <<< "$env" + + for str in "${block_list[@]}"; do + if [[ "$key" == "$str" ]]; then + blocked=true + break + fi + done + + if [ "$blocked" == "false" ]; then + path="${ENV_DIR}/${key}" + echo -n "$value" > "$path" + fi + done + + LAYERS_DIR=/tmp/.shp/layers + CACHE_DIR=/tmp/.shp/cache + + mkdir -p "$CACHE_DIR" "$LAYERS_DIR" + + function announce_phase { + printf "===> %s\n" "$1" + } + + announce_phase "ANALYZING" + /cnb/lifecycle/analyzer -layers="$LAYERS_DIR" "${PARAM_OUTPUT_IMAGE}" + + announce_phase "DETECTING" + /cnb/lifecycle/detector -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + announce_phase "RESTORING" + /cnb/lifecycle/restorer -cache-dir="$CACHE_DIR" -layers="$LAYERS_DIR" + + announce_phase "BUILDING" + /cnb/lifecycle/builder -app="${PARAM_SOURCE_CONTEXT}" -layers="$LAYERS_DIR" + + exporter_args=( -layers="$LAYERS_DIR" -report=/tmp/report.toml -cache-dir="$CACHE_DIR" -app="${PARAM_SOURCE_CONTEXT}") + grep -q "buildpack-default-process-type" "$LAYERS_DIR/config/metadata.toml" || exporter_args+=( -process-type web ) + + announce_phase "EXPORTING" + /cnb/lifecycle/exporter "${exporter_args[@]}" "${PARAM_OUTPUT_IMAGE}" + + # Store the image digest + grep digest /tmp/report.toml | tail -n 1 | tr -d ' \"\n' | sed s/digest=// > "$(results.shp-image-digest.path)" + volumeMounts: + - mountPath: /platform/env + name: platform-env + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 1001 + runAsGroup: 1000 diff --git a/samples/v1beta1/buildstrategy/kaniko/buildstrategy_kaniko-trivy_cr.yaml b/samples/v1beta1/buildstrategy/kaniko/buildstrategy_kaniko-trivy_cr.yaml new file mode 100644 index 000000000..4db569273 --- /dev/null +++ b/samples/v1beta1/buildstrategy/kaniko/buildstrategy_kaniko-trivy_cr.yaml @@ -0,0 +1,87 @@ +# This Build Strategy will intentionally fail if the image has any +# critical CVEs. It will not be pushed into the destination registry +# if any critical vulnerabilities are found. +--- +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: kaniko-trivy +spec: + volumes: + - name: layout + emptyDir: {} + - name: tar + emptyDir: {} + steps: + - name: kaniko-build + image: gcr.io/kaniko-project/executor:v1.16.0 + workingDir: $(params.shp-source-root) + securityContext: + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - SETFCAP + - KILL + env: + - name: HOME + value: /tekton/home + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + command: + - /kaniko/executor + args: + - --dockerfile + - $(params.dockerfile) + - --context + - $(params.shp-source-context) + - --destination + - $(params.shp-output-image) + - --snapshot-mode + - redo + - --no-push + - --tar-path + - $(params.shp-output-directory)/image.tar + # https://github.com/GoogleContainerTools/kaniko/issues/2164 + - --ignore-path + - /product_uuid + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + - name: trivy-scan + image: docker.io/aquasec/trivy:0.46.0 + command: + - trivy + args: + - image + - --exit-code=1 + - --severity=CRITICAL + - --input + - $(params.shp-output-directory)/image.tar + env: + - name: HOME + value: /tekton/home + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + parameters: + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: "Dockerfile" + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1beta1/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml b/samples/v1beta1/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml new file mode 100644 index 000000000..ac35bc0f3 --- /dev/null +++ b/samples/v1beta1/buildstrategy/kaniko/buildstrategy_kaniko_cr.yaml @@ -0,0 +1,61 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: kaniko +spec: + steps: + - name: build-and-push + image: gcr.io/kaniko-project/executor:v1.16.0 + workingDir: $(params.shp-source-root) + securityContext: + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - SETFCAP + - KILL + env: + - name: HOME + value: /tekton/home + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + command: + - /kaniko/executor + args: + - --dockerfile + - $(params.dockerfile) + - --context + - $(params.shp-source-context) + - --destination + - $(params.shp-output-image) + - --snapshot-mode + - redo + - --no-push + - --tar-path + - $(params.shp-output-directory)/image.tar + # https://github.com/GoogleContainerTools/kaniko/issues/2164 + - --ignore-path + - /product_uuid + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + parameters: + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: "Dockerfile" + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1beta1/buildstrategy/ko/buildstrategy_ko_cr.yaml b/samples/v1beta1/buildstrategy/ko/buildstrategy_ko_cr.yaml new file mode 100644 index 000000000..bafc08ff1 --- /dev/null +++ b/samples/v1beta1/buildstrategy/ko/buildstrategy_ko_cr.yaml @@ -0,0 +1,116 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: ko +spec: + parameters: + - name: go-flags + description: "Value for the GOFLAGS environment variable." + default: "" + - name: go-version + description: "Version of Go, must match a tag from https://hub.docker.com/_/golang?tab=tags" + default: "1.20" + - name: ko-version + description: "Version of ko, must be either 'latest', or a release name from https://github.com/ko-build/ko/releases" + default: latest + - name: package-directory + description: "The directory inside the context directory containing the main package." + default: "." + - name: target-platform + description: "Target platform to be built. For example: 'linux/arm64'. Multiple platforms can be provided separated by comma, for example: 'linux/arm64,linux/amd64'. The value 'all' will build all platforms supported by the base image. The value 'current' will build the platform on which the build runs." + default: current + volumes: + - name: gocache + description: "Volume to contain the GOCACHE. Can be set to a persistent volume to optimize compilation performance for rebuilds." + overridable: true + emptyDir: {} + steps: + - name: build + image: golang:$(params.go-version) + imagePullPolicy: Always + workingDir: $(params.shp-source-root) + volumeMounts: + - mountPath: /gocache + name: gocache + readOnly: false + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + - name: GOFLAGS + value: $(params.go-flags) + - name: GOCACHE + value: /gocache + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + - name: PARAM_OUTPUT_DIRECTORY + value: $(params.shp-output-directory) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_SOURCE_ROOT + value: $(params.shp-source-root) + - name: PARAM_TARGET_PLATFORM + value: $(params.target-platform) + - name: PARAM_PACKAGE_DIRECTORY + value: $(params.package-directory) + - name: PARAM_KO_VERSION + value: $(params.ko-version) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Determine the ko version + KO_VERSION="${PARAM_KO_VERSION}" + if [ "${KO_VERSION}" == "latest" ]; then + KO_VERSION=$(curl --silent "https://api.github.com/repos/ko-build/ko/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + fi + + # Create one variable with v-suffix and one without as we need both for the download URL + if [[ ${KO_VERSION} = v* ]]; then + KO_VERSION_WITH_V=${KO_VERSION} + KO_VERSION_WITHOUT_V=${KO_VERSION:1} + else + KO_VERSION_WITH_V=v${KO_VERSION} + KO_VERSION_WITHOUT_V=${KO_VERSION} + fi + + # Download ko to the temp directory + curl -f -s -L "https://github.com/ko-build/ko/releases/download/${KO_VERSION_WITH_V}/ko_${KO_VERSION_WITHOUT_V}_$(uname)_$(uname -m | sed 's/aarch64/arm64/').tar.gz" | tar xzf - -C /tmp ko + + # Determine the platform + PLATFORM="${PARAM_TARGET_PLATFORM}" + if [ "${PLATFORM}" == "current" ]; then + PLATFORM="$(uname | tr '[:upper:]' '[:lower:]')/$(uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/')" + fi + + # Print version information + go version + echo "ko version $(/tmp/ko version)" + + # Allow directory to be owned by other user which is normal for a volume-mounted directory. + # This allows Go to run git commands to access repository metadata. + # Documentation: https://git-scm.com/docs/git-config/2.39.0#Documentation/git-config.txt-safedirectory + git config --global --add safe.directory "${PARAM_SOURCE_ROOT}" + + # Run ko + + export GOROOT="$(go env GOROOT)" + + pushd "${PARAM_SOURCE_CONTEXT}" > /dev/null + /tmp/ko build "${PARAM_PACKAGE_DIRECTORY}" --oci-layout-path="${PARAM_OUTPUT_DIRECTORY}" --platform="${PLATFORM}" --push=false + popd > /dev/null + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + securityContext: + runAsUser: 1000 + runAsGroup: 1000 diff --git a/samples/v1beta1/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml b/samples/v1beta1/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml new file mode 100644 index 000000000..c2be5b14a --- /dev/null +++ b/samples/v1beta1/buildstrategy/source-to-image/buildstrategy_source-to-image-redhat_cr.yaml @@ -0,0 +1,163 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: source-to-image-redhat +spec: + volumes: + - name: s2i + emptyDir: {} + steps: + - name: s2i-generate + image: registry.redhat.io/ocp-tools-43-tech-preview/source-to-image-rhel8:latest + workingDir: $(params.shp-source-root) + command: + - /usr/local/bin/s2i + args: + - build + - $(params.shp-source-context) + - $(params.builder-image) + - $(params.shp-output-image) + - --as-dockerfile=/s2i/Dockerfile + volumeMounts: + - name: s2i + mountPath: /s2i + - name: buildah + image: quay.io/containers/buildah:v1.32.0 + workingDir: /s2i + securityContext: + capabilities: + add: + - "SETFCAP" + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Parse parameters + image= + target= + registriesBlock="" + inRegistriesBlock=false + registriesInsecure="" + inRegistriesInsecure=false + registriesSearch="" + inRegistriesSearch=false + while [[ $# -gt 0 ]]; do + arg="$1" + shift + + if [ "${arg}" == "--image" ]; then + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + image="$1" + shift + elif [ "${arg}" == "--target" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + target="$1" + shift + elif [ "${arg}" == "--registries-block" ]; then + inRegistriesBlock=true + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-insecure" ]; then + inRegistriesInsecure=true + inRegistriesBlock=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-search" ]; then + inRegistriesSearch=true + inRegistriesBlock=false + inRegistriesInsecure=false + elif [ "${inRegistriesBlock}" == "true" ]; then + registriesBlock="${registriesBlock}'${arg}', " + elif [ "${inRegistriesInsecure}" == "true" ]; then + registriesInsecure="${registriesInsecure}'${arg}', " + elif [ "${inRegistriesSearch}" == "true" ]; then + registriesSearch="${registriesSearch}'${arg}', " + else + echo "Invalid usage" + exit 1 + fi + done + + echo "[INFO] Creating registries config file..." + if [ "${registriesSearch}" != "" ]; then + cat <>/tmp/registries.conf + [registries.search] + registries = [${registriesSearch::-2}] + + EOF + fi + if [ "${registriesInsecure}" != "" ]; then + cat <>/tmp/registries.conf + [registries.insecure] + registries = [${registriesInsecure::-2}] + + EOF + fi + if [ "${registriesBlock}" != "" ]; then + cat <>/tmp/registries.conf + [registries.block] + registries = [${registriesBlock::-2}] + + EOF + fi + + # Building the image + echo "[INFO] Building image ${image}" + buildah --storage-driver=$(params.storage-driver) bud \ + --registries-conf=/tmp/registries.conf \ + --tag="${image}" + + # Write the image + echo "[INFO] Writing image ${image}" + buildah --storage-driver=$(params.storage-driver) push \ + "${image}" \ + "oci:${target}" + # That's the separator between the shell script and its args + - -- + - --image + - $(params.shp-output-image) + - --registries-block + - $(params.registries-block[*]) + - --registries-insecure + - $(params.registries-insecure[*]) + - --registries-search + - $(params.registries-search[*]) + - --target + - $(params.shp-output-directory) + volumeMounts: + - name: s2i + mountPath: /s2i + parameters: + - name: registries-block + description: The registries that need to block pull access. + type: array + defaults: [] + - name: registries-insecure + description: The fully-qualified name of insecure registries. An insecure registry is one that does not have a valid SSL certificate or only supports HTTP. + type: array + defaults: [] + - name: registries-search + description: The registries for searching short name images such as `golang:latest`. + type: array + defaults: + - docker.io + - quay.io + - name: builder-image + description: The builder image. + type: string + - name: storage-driver + description: "The storage driver to use, such as 'overlay' or 'vfs'." + type: string + default: "vfs" + # For details see the "--storage-driver" section of https://github.com/containers/buildah/blob/main/docs/buildah.1.md#options + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/samples/v1beta1/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml b/samples/v1beta1/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml new file mode 100644 index 000000000..22fd52e45 --- /dev/null +++ b/samples/v1beta1/buildstrategy/source-to-image/buildstrategy_source-to-image_cr.yaml @@ -0,0 +1,73 @@ +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: source-to-image +spec: + volumes: + - name: gen-source + emptyDir: {} + steps: + - command: + - /usr/local/bin/s2i + - build + - $(params.shp-source-context) + - $(params.builder-image) + - '--as-dockerfile' + - /gen-source/Dockerfile.gen + image: quay.io/openshift-pipeline/s2i:nightly + imagePullPolicy: Always + name: s2i-build-as-dockerfile + volumeMounts: + - mountPath: /gen-source + name: gen-source + workingDir: $(params.shp-source-root) + - name: build-and-push + image: gcr.io/kaniko-project/executor:v1.16.0 + command: + - /kaniko/executor + args: + - --dockerfile + - /gen-source/Dockerfile.gen + - --context + - /gen-source + - --destination + - $(params.shp-output-image) + - --snapshot-mode + - redo + - --no-push + - --tar-path + - $(params.shp-output-directory)/image.tar + # https://github.com/GoogleContainerTools/kaniko/issues/2164 + - --ignore-path + - /product_uuid + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - SETFCAP + - KILL + volumeMounts: + - mountPath: /gen-source + name: gen-source + workingDir: /gen-source + parameters: + - name: builder-image + description: The builder image. + type: string + securityContext: + runAsUser: 0 + runAsGroup: 0 diff --git a/test/data/build_buildah_cr_custom_context+dockerfile.yaml b/test/data/v1alpha1/build_buildah_cr_custom_context+dockerfile.yaml similarity index 100% rename from test/data/build_buildah_cr_custom_context+dockerfile.yaml rename to test/data/v1alpha1/build_buildah_cr_custom_context+dockerfile.yaml diff --git a/test/data/build_buildah_cr_local_source_upload.yaml b/test/data/v1alpha1/build_buildah_cr_local_source_upload.yaml similarity index 100% rename from test/data/build_buildah_cr_local_source_upload.yaml rename to test/data/v1alpha1/build_buildah_cr_local_source_upload.yaml diff --git a/test/data/build_buildah_cr_mutate.yaml b/test/data/v1alpha1/build_buildah_cr_mutate.yaml similarity index 100% rename from test/data/build_buildah_cr_mutate.yaml rename to test/data/v1alpha1/build_buildah_cr_mutate.yaml diff --git a/test/data/build_buildah_cr_private_github.yaml b/test/data/v1alpha1/build_buildah_cr_private_github.yaml similarity index 100% rename from test/data/build_buildah_cr_private_github.yaml rename to test/data/v1alpha1/build_buildah_cr_private_github.yaml diff --git a/test/data/build_buildah_cr_private_gitlab.yaml b/test/data/v1alpha1/build_buildah_cr_private_gitlab.yaml similarity index 100% rename from test/data/build_buildah_cr_private_gitlab.yaml rename to test/data/v1alpha1/build_buildah_cr_private_gitlab.yaml diff --git a/test/data/build_buildkit_cr_insecure_registry.yaml b/test/data/v1alpha1/build_buildkit_cr_insecure_registry.yaml similarity index 100% rename from test/data/build_buildkit_cr_insecure_registry.yaml rename to test/data/v1alpha1/build_buildkit_cr_insecure_registry.yaml diff --git a/test/data/build_buildpacks-v3_golang_cr.yaml b/test/data/v1alpha1/build_buildpacks-v3_golang_cr.yaml similarity index 100% rename from test/data/build_buildpacks-v3_golang_cr.yaml rename to test/data/v1alpha1/build_buildpacks-v3_golang_cr.yaml diff --git a/test/data/build_buildpacks-v3_golang_cr_env.yaml b/test/data/v1alpha1/build_buildpacks-v3_golang_cr_env.yaml similarity index 100% rename from test/data/build_buildpacks-v3_golang_cr_env.yaml rename to test/data/v1alpha1/build_buildpacks-v3_golang_cr_env.yaml diff --git a/test/data/build_buildpacks-v3_golang_delete_cr.yaml b/test/data/v1alpha1/build_buildpacks-v3_golang_delete_cr.yaml similarity index 100% rename from test/data/build_buildpacks-v3_golang_delete_cr.yaml rename to test/data/v1alpha1/build_buildpacks-v3_golang_delete_cr.yaml diff --git a/test/data/build_buildpacks-v3_java_cr.yaml b/test/data/v1alpha1/build_buildpacks-v3_java_cr.yaml similarity index 100% rename from test/data/build_buildpacks-v3_java_cr.yaml rename to test/data/v1alpha1/build_buildpacks-v3_java_cr.yaml diff --git a/test/data/build_buildpacks-v3_php_cr.yaml b/test/data/v1alpha1/build_buildpacks-v3_php_cr.yaml similarity index 100% rename from test/data/build_buildpacks-v3_php_cr.yaml rename to test/data/v1alpha1/build_buildpacks-v3_php_cr.yaml diff --git a/test/data/build_buildpacks-v3_ruby_cr.yaml b/test/data/v1alpha1/build_buildpacks-v3_ruby_cr.yaml similarity index 100% rename from test/data/build_buildpacks-v3_ruby_cr.yaml rename to test/data/v1alpha1/build_buildpacks-v3_ruby_cr.yaml diff --git a/test/data/build_kaniko_cr_advanced_dockerfile.yaml b/test/data/v1alpha1/build_kaniko_cr_advanced_dockerfile.yaml similarity index 100% rename from test/data/build_kaniko_cr_advanced_dockerfile.yaml rename to test/data/v1alpha1/build_kaniko_cr_advanced_dockerfile.yaml diff --git a/test/data/build_kaniko_cr_custom_context+dockerfile.yaml b/test/data/v1alpha1/build_kaniko_cr_custom_context+dockerfile.yaml similarity index 100% rename from test/data/build_kaniko_cr_custom_context+dockerfile.yaml rename to test/data/v1alpha1/build_kaniko_cr_custom_context+dockerfile.yaml diff --git a/test/data/build_kaniko_cr_private_github.yaml b/test/data/v1alpha1/build_kaniko_cr_private_github.yaml similarity index 100% rename from test/data/build_kaniko_cr_private_github.yaml rename to test/data/v1alpha1/build_kaniko_cr_private_github.yaml diff --git a/test/data/build_kaniko_cr_private_gitlab.yaml b/test/data/v1alpha1/build_kaniko_cr_private_gitlab.yaml similarity index 100% rename from test/data/build_kaniko_cr_private_gitlab.yaml rename to test/data/v1alpha1/build_kaniko_cr_private_gitlab.yaml diff --git a/test/data/build_non_existing_repo.yaml b/test/data/v1alpha1/build_non_existing_repo.yaml similarity index 100% rename from test/data/build_non_existing_repo.yaml rename to test/data/v1alpha1/build_non_existing_repo.yaml diff --git a/test/data/build_source-to-image_cr_private_github.yaml b/test/data/v1alpha1/build_source-to-image_cr_private_github.yaml similarity index 100% rename from test/data/build_source-to-image_cr_private_github.yaml rename to test/data/v1alpha1/build_source-to-image_cr_private_github.yaml diff --git a/test/data/buildrun_buildah_cr_custom_context+dockerfile.yaml b/test/data/v1alpha1/buildrun_buildah_cr_custom_context+dockerfile.yaml similarity index 100% rename from test/data/buildrun_buildah_cr_custom_context+dockerfile.yaml rename to test/data/v1alpha1/buildrun_buildah_cr_custom_context+dockerfile.yaml diff --git a/test/data/buildrun_buildah_cr_local_source_upload.yaml b/test/data/v1alpha1/buildrun_buildah_cr_local_source_upload.yaml similarity index 100% rename from test/data/buildrun_buildah_cr_local_source_upload.yaml rename to test/data/v1alpha1/buildrun_buildah_cr_local_source_upload.yaml diff --git a/test/data/buildrun_buildah_cr_mutate.yaml b/test/data/v1alpha1/buildrun_buildah_cr_mutate.yaml similarity index 100% rename from test/data/buildrun_buildah_cr_mutate.yaml rename to test/data/v1alpha1/buildrun_buildah_cr_mutate.yaml diff --git a/test/data/buildrun_buildpacks-v3_golang_cr.yaml b/test/data/v1alpha1/buildrun_buildpacks-v3_golang_cr.yaml similarity index 100% rename from test/data/buildrun_buildpacks-v3_golang_cr.yaml rename to test/data/v1alpha1/buildrun_buildpacks-v3_golang_cr.yaml diff --git a/test/data/buildrun_buildpacks-v3_java_cr.yaml b/test/data/v1alpha1/buildrun_buildpacks-v3_java_cr.yaml similarity index 100% rename from test/data/buildrun_buildpacks-v3_java_cr.yaml rename to test/data/v1alpha1/buildrun_buildpacks-v3_java_cr.yaml diff --git a/test/data/buildrun_buildpacks-v3_nodejs_runtime-image_cr.yaml b/test/data/v1alpha1/buildrun_buildpacks-v3_nodejs_runtime-image_cr.yaml similarity index 100% rename from test/data/buildrun_buildpacks-v3_nodejs_runtime-image_cr.yaml rename to test/data/v1alpha1/buildrun_buildpacks-v3_nodejs_runtime-image_cr.yaml diff --git a/test/data/buildrun_buildpacks-v3_php_cr.yaml b/test/data/v1alpha1/buildrun_buildpacks-v3_php_cr.yaml similarity index 100% rename from test/data/buildrun_buildpacks-v3_php_cr.yaml rename to test/data/v1alpha1/buildrun_buildpacks-v3_php_cr.yaml diff --git a/test/data/buildrun_buildpacks-v3_ruby_cr.yaml b/test/data/v1alpha1/buildrun_buildpacks-v3_ruby_cr.yaml similarity index 100% rename from test/data/buildrun_buildpacks-v3_ruby_cr.yaml rename to test/data/v1alpha1/buildrun_buildpacks-v3_ruby_cr.yaml diff --git a/test/data/buildrun_kaniko_cr_advanced_dockerfile.yaml b/test/data/v1alpha1/buildrun_kaniko_cr_advanced_dockerfile.yaml similarity index 100% rename from test/data/buildrun_kaniko_cr_advanced_dockerfile.yaml rename to test/data/v1alpha1/buildrun_kaniko_cr_advanced_dockerfile.yaml diff --git a/test/data/buildrun_kaniko_cr_custom_context+dockerfile.yaml b/test/data/v1alpha1/buildrun_kaniko_cr_custom_context+dockerfile.yaml similarity index 100% rename from test/data/buildrun_kaniko_cr_custom_context+dockerfile.yaml rename to test/data/v1alpha1/buildrun_kaniko_cr_custom_context+dockerfile.yaml diff --git a/test/data/buildrun_non_existing_repo.yaml b/test/data/v1alpha1/buildrun_non_existing_repo.yaml similarity index 100% rename from test/data/buildrun_non_existing_repo.yaml rename to test/data/v1alpha1/buildrun_non_existing_repo.yaml diff --git a/test/data/v1beta1/build_buildah_cr_custom_context+dockerfile.yaml b/test/data/v1beta1/build_buildah_cr_custom_context+dockerfile.yaml new file mode 100644 index 000000000..84dce7465 --- /dev/null +++ b/test/data/v1beta1/build_buildah_cr_custom_context+dockerfile.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah-custom-context-dockerfile +spec: + source: + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: docker-build-renamed + strategy: + name: buildah-shipwright-managed-push + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: RenamedDockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/renamed-dockerfile diff --git a/test/data/v1beta1/build_buildah_cr_local_source_upload.yaml b/test/data/v1beta1/build_buildah_cr_local_source_upload.yaml new file mode 100644 index 000000000..4a0e5f160 --- /dev/null +++ b/test/data/v1beta1/build_buildah_cr_local_source_upload.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah-golang-build-local-source-upload +spec: + source: + git: + url: https://github.com/shipwright-io/sample-go + contextDir: docker-build + strategy: + name: buildah-shipwright-managed-push + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_buildah_cr_mutate.yaml b/test/data/v1beta1/build_buildah_cr_mutate.yaml new file mode 100644 index 000000000..ae7cb9b5c --- /dev/null +++ b/test/data/v1beta1/build_buildah_cr_mutate.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah-golang-build-mutate +spec: + source: + git: + url: https://github.com/shipwright-io/sample-go + contextDir: docker-build + strategy: + name: buildah-shipwright-managed-push + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app + annotations: + "org.opencontainers.image.url": "https://my-company.com/images" + labels: + "maintainer": "team@my-company.com" diff --git a/test/data/v1beta1/build_buildah_cr_private_github.yaml b/test/data/v1beta1/build_buildah_cr_private_github.yaml new file mode 100644 index 000000000..81ef768b7 --- /dev/null +++ b/test/data/v1beta1/build_buildah_cr_private_github.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah-golang-build +spec: + source: + git: + url: git@github.com:qu1queee/newtaxi.git + cloneSecret: github-ssh-all + strategy: + name: buildah-shipwright-managed-push + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_buildah_cr_private_gitlab.yaml b/test/data/v1beta1/build_buildah_cr_private_gitlab.yaml new file mode 100644 index 000000000..13b19a069 --- /dev/null +++ b/test/data/v1beta1/build_buildah_cr_private_gitlab.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah-golang-build +spec: + source: + git: + url: git@gitlab.com:eduardooli/newtaxi.git + strategy: + name: buildah-shipwright-managed-push + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_buildkit_cr_insecure_registry.yaml b/test/data/v1beta1/build_buildkit_cr_insecure_registry.yaml new file mode 100644 index 000000000..a870298db --- /dev/null +++ b/test/data/v1beta1/build_buildkit_cr_insecure_registry.yaml @@ -0,0 +1,24 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildkit-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-go + contextDir: docker-build + paramValues: + - name: insecure-registry + value: "true" + - name: platforms + values: + - value: linux/amd64 + - value: linux/arm64 + strategy: + name: buildkit + kind: ClusterBuildStrategy + retention: + atBuildDeletion: true + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app \ No newline at end of file diff --git a/test/data/v1beta1/build_buildpacks-v3_golang_cr.yaml b/test/data/v1beta1/build_buildpacks-v3_golang_cr.yaml new file mode 100644 index 000000000..f097c9aba --- /dev/null +++ b/test/data/v1beta1/build_buildpacks-v3_golang_cr.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpack-golang-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-go + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_buildpacks-v3_golang_cr_env.yaml b/test/data/v1beta1/build_buildpacks-v3_golang_cr_env.yaml new file mode 100644 index 000000000..9806e5114 --- /dev/null +++ b/test/data/v1beta1/build_buildpacks-v3_golang_cr_env.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpack-golang-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-go + contextDir: source-build-with-package + env: + - name: BP_GO_TARGETS + value: "main-package" + strategy: + name: buildpacks-v3 + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_buildpacks-v3_golang_delete_cr.yaml b/test/data/v1beta1/build_buildpacks-v3_golang_delete_cr.yaml new file mode 100644 index 000000000..482642615 --- /dev/null +++ b/test/data/v1beta1/build_buildpacks-v3_golang_delete_cr.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpack-golang-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-go + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: ClusterBuildStrategy + retention: + atBuildDeletion: true + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_buildpacks-v3_java_cr.yaml b/test/data/v1beta1/build_buildpacks-v3_java_cr.yaml new file mode 100644 index 000000000..8628426e2 --- /dev/null +++ b/test/data/v1beta1/build_buildpacks-v3_java_cr.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpack-java-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-java + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_buildpacks-v3_php_cr.yaml b/test/data/v1beta1/build_buildpacks-v3_php_cr.yaml new file mode 100644 index 000000000..4966f5b6a --- /dev/null +++ b/test/data/v1beta1/build_buildpacks-v3_php_cr.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpack-php-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-php + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_buildpacks-v3_ruby_cr.yaml b/test/data/v1beta1/build_buildpacks-v3_ruby_cr.yaml new file mode 100644 index 000000000..440dc37c9 --- /dev/null +++ b/test/data/v1beta1/build_buildpacks-v3_ruby_cr.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpack-ruby-build +spec: + source: + git: + url: https://github.com/shipwright-io/sample-ruby + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_kaniko_cr_advanced_dockerfile.yaml b/test/data/v1beta1/build_kaniko_cr_advanced_dockerfile.yaml new file mode 100644 index 000000000..46818a38c --- /dev/null +++ b/test/data/v1beta1/build_kaniko_cr_advanced_dockerfile.yaml @@ -0,0 +1,20 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: kaniko-advanced-dockerfile +spec: + source: + git: + url: https://github.com/shipwright-io/sample-java + contextDir: docker-build + strategy: + name: kaniko + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + retention: + atBuildDeletion: false + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/advanced-dockerfile diff --git a/test/data/v1beta1/build_kaniko_cr_custom_context+dockerfile.yaml b/test/data/v1beta1/build_kaniko_cr_custom_context+dockerfile.yaml new file mode 100644 index 000000000..2d6ccc596 --- /dev/null +++ b/test/data/v1beta1/build_kaniko_cr_custom_context+dockerfile.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: kaniko-custom-context-dockerfile +spec: + source: + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: docker-build + strategy: + name: kaniko + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/renamed-dockerfile diff --git a/test/data/v1beta1/build_kaniko_cr_private_github.yaml b/test/data/v1beta1/build_kaniko_cr_private_github.yaml new file mode 100644 index 000000000..26fdfd6e3 --- /dev/null +++ b/test/data/v1beta1/build_kaniko_cr_private_github.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: kaniko-golang-build +spec: + source: + git: + url: git@github.com:qu1queee/newtaxi.git + strategy: + name: kaniko + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_kaniko_cr_private_gitlab.yaml b/test/data/v1beta1/build_kaniko_cr_private_gitlab.yaml new file mode 100644 index 000000000..a0a4c8750 --- /dev/null +++ b/test/data/v1beta1/build_kaniko_cr_private_gitlab.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: kaniko-golang-build +spec: + source: + git: + url: git@gitlab.com:eduardooli/newtaxi.git + strategy: + name: kaniko + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/taxi-app diff --git a/test/data/v1beta1/build_non_existing_repo.yaml b/test/data/v1beta1/build_non_existing_repo.yaml new file mode 100644 index 000000000..6c8869fd6 --- /dev/null +++ b/test/data/v1beta1/build_non_existing_repo.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: build-non-existing-repo +spec: + source: + git: + url: https://github.com/shipwright-io/sample-nodejs-no-exists + strategy: + name: kaniko + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/no-exists + diff --git a/test/data/v1beta1/build_source-to-image_cr_private_github.yaml b/test/data/v1beta1/build_source-to-image_cr_private_github.yaml new file mode 100644 index 000000000..747de724b --- /dev/null +++ b/test/data/v1beta1/build_source-to-image_cr_private_github.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: s2i-nodejs-build +spec: + source: + git: + url: git@github.com:shipwright-io/sample-nodejs-private.git + strategy: + name: source-to-image + kind: ClusterBuildStrategy + paramValues: + - name: builder-image + value: docker.io/centos/nodejs-10-centos7 + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/nodejs-ex diff --git a/test/data/v1beta1/buildrun_buildah_cr_custom_context+dockerfile.yaml b/test/data/v1beta1/buildrun_buildah_cr_custom_context+dockerfile.yaml new file mode 100644 index 000000000..c3b2201be --- /dev/null +++ b/test/data/v1beta1/buildrun_buildah_cr_custom_context+dockerfile.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-custom-context-dockerfile +spec: + build: + name: buildah-custom-context-dockerfile diff --git a/test/data/v1beta1/buildrun_buildah_cr_local_source_upload.yaml b/test/data/v1beta1/buildrun_buildah_cr_local_source_upload.yaml new file mode 100644 index 000000000..33e531c84 --- /dev/null +++ b/test/data/v1beta1/buildrun_buildah_cr_local_source_upload.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-golang-local-source-upload +spec: + build: + name: buildah-local-source-upload + sources: + - name: local-copy diff --git a/test/data/v1beta1/buildrun_buildah_cr_mutate.yaml b/test/data/v1beta1/buildrun_buildah_cr_mutate.yaml new file mode 100644 index 000000000..4bc58d2c9 --- /dev/null +++ b/test/data/v1beta1/buildrun_buildah_cr_mutate.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-golang-build-mutate +spec: + build: + name: buildah-golang-build-mutate diff --git a/test/data/v1beta1/buildrun_buildpacks-v3_golang_cr.yaml b/test/data/v1beta1/buildrun_buildpacks-v3_golang_cr.yaml new file mode 100644 index 000000000..da9864789 --- /dev/null +++ b/test/data/v1beta1/buildrun_buildpacks-v3_golang_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildpack-golang-buildrun +spec: + build: + name: buildpack-golang-build + serviceAccount: ".generate" diff --git a/test/data/v1beta1/buildrun_buildpacks-v3_java_cr.yaml b/test/data/v1beta1/buildrun_buildpacks-v3_java_cr.yaml new file mode 100644 index 000000000..294697caf --- /dev/null +++ b/test/data/v1beta1/buildrun_buildpacks-v3_java_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildpack-java-buildrun +spec: + build: + name: buildpack-java-build + serviceAccount: ".generate" diff --git a/test/data/v1beta1/buildrun_buildpacks-v3_nodejs_runtime-image_cr.yaml b/test/data/v1beta1/buildrun_buildpacks-v3_nodejs_runtime-image_cr.yaml new file mode 100644 index 000000000..074f6dea5 --- /dev/null +++ b/test/data/v1beta1/buildrun_buildpacks-v3_nodejs_runtime-image_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildpack-nodejs-buildrun-heroku +spec: + build: + name: buildpack-nodejs-build-heroku + serviceAccount: ".generate" diff --git a/test/data/v1beta1/buildrun_buildpacks-v3_php_cr.yaml b/test/data/v1beta1/buildrun_buildpacks-v3_php_cr.yaml new file mode 100644 index 000000000..0bcd8731d --- /dev/null +++ b/test/data/v1beta1/buildrun_buildpacks-v3_php_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildpack-php-buildrun +spec: + build: + name: buildpack-php-build + serviceAccount: ".generate" diff --git a/test/data/v1beta1/buildrun_buildpacks-v3_ruby_cr.yaml b/test/data/v1beta1/buildrun_buildpacks-v3_ruby_cr.yaml new file mode 100644 index 000000000..d8a6a1cfd --- /dev/null +++ b/test/data/v1beta1/buildrun_buildpacks-v3_ruby_cr.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildpack-ruby-buildrun +spec: + build: + name: buildpack-ruby-build + serviceAccount: ".generate" diff --git a/test/data/v1beta1/buildrun_kaniko_cr_advanced_dockerfile.yaml b/test/data/v1beta1/buildrun_kaniko_cr_advanced_dockerfile.yaml new file mode 100644 index 000000000..9a754e63b --- /dev/null +++ b/test/data/v1beta1/buildrun_kaniko_cr_advanced_dockerfile.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: kaniko-advanced-dockerfile +spec: + build: + name: kaniko-advanced-dockerfile diff --git a/test/data/v1beta1/buildrun_kaniko_cr_custom_context+dockerfile.yaml b/test/data/v1beta1/buildrun_kaniko_cr_custom_context+dockerfile.yaml new file mode 100644 index 000000000..ba796cabb --- /dev/null +++ b/test/data/v1beta1/buildrun_kaniko_cr_custom_context+dockerfile.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: kaniko-custom-context-dockerfile +spec: + build: + name: kaniko-custom-context-dockerfile diff --git a/test/data/v1beta1/buildrun_non_existing_repo.yaml b/test/data/v1beta1/buildrun_non_existing_repo.yaml new file mode 100644 index 000000000..eac965a18 --- /dev/null +++ b/test/data/v1beta1/buildrun_non_existing_repo.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildrun-non-existing-repo +spec: + build: + name: buildrun-non-existing-repo diff --git a/test/e2e/common_suite_test.go b/test/e2e/v1alpha1/common_suite_test.go similarity index 98% rename from test/e2e/common_suite_test.go rename to test/e2e/v1alpha1/common_suite_test.go index 14a166fc4..b4b453d21 100644 --- a/test/e2e/common_suite_test.go +++ b/test/e2e/v1alpha1/common_suite_test.go @@ -17,7 +17,7 @@ import ( core "k8s.io/api/core/v1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" ) @@ -73,7 +73,7 @@ func (b *buildPrototype) SourceCredentials(name string) *buildPrototype { } func (b *buildPrototype) SourceGit(repository string) *buildPrototype { - b.build.Spec.Source.URL = pointer.String(repository) + b.build.Spec.Source.URL = ptr.To[string](repository) b.build.Spec.Source.BundleContainer = nil return b } @@ -95,7 +95,7 @@ func (b *buildPrototype) SourceBundlePrune(prune buildv1alpha1.PruneOption) *bui } func (b *buildPrototype) SourceContextDir(contextDir string) *buildPrototype { - b.build.Spec.Source.ContextDir = pointer.String(contextDir) + b.build.Spec.Source.ContextDir = ptr.To[string](contextDir) return b } @@ -280,7 +280,7 @@ func (b *buildRunPrototype) GenerateServiceAccount() *buildRunPrototype { if b.buildRun.Spec.ServiceAccount == nil { b.buildRun.Spec.ServiceAccount = &buildv1alpha1.ServiceAccount{} } - b.buildRun.Spec.ServiceAccount.Generate = pointer.Bool(true) + b.buildRun.Spec.ServiceAccount.Generate = ptr.To[bool](true) return b } diff --git a/test/e2e/common_test.go b/test/e2e/v1alpha1/common_test.go similarity index 99% rename from test/e2e/common_test.go rename to test/e2e/v1alpha1/common_test.go index 87443e450..4c3c9ba7c 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/v1alpha1/common_test.go @@ -25,7 +25,7 @@ import ( buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" "github.com/shipwright-io/build/pkg/reconciler/buildrun/resources" - "github.com/shipwright-io/build/test/utils" + utils "github.com/shipwright-io/build/test/utils/v1alpha1" ) func generateTestID(id string) string { diff --git a/test/e2e/e2e_bundle_test.go b/test/e2e/v1alpha1/e2e_bundle_test.go similarity index 100% rename from test/e2e/e2e_bundle_test.go rename to test/e2e/v1alpha1/e2e_bundle_test.go diff --git a/test/e2e/e2e_image_mutate_test.go b/test/e2e/v1alpha1/e2e_image_mutate_test.go similarity index 94% rename from test/e2e/e2e_image_mutate_test.go rename to test/e2e/v1alpha1/e2e_image_mutate_test.go index 04fd00572..d2fdacb5f 100644 --- a/test/e2e/e2e_image_mutate_test.go +++ b/test/e2e/v1alpha1/e2e_image_mutate_test.go @@ -46,14 +46,14 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildah_cr_mutate.yaml", + "test/data/v1alpha1/build_buildah_cr_mutate.yaml", ) }) It("should mutate an image with annotation and label", func() { buildRun, err = buildRunTestData( testBuild.Namespace, testID, - "test/data/buildrun_buildah_cr_mutate.yaml", + "test/data/v1alpha1/buildrun_buildah_cr_mutate.yaml", ) Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") appendRegistryInsecureParamValue(build, buildRun) diff --git a/test/e2e/e2e_local_source_upload_test.go b/test/e2e/v1alpha1/e2e_local_source_upload_test.go similarity index 93% rename from test/e2e/e2e_local_source_upload_test.go rename to test/e2e/v1alpha1/e2e_local_source_upload_test.go index d3972fa4b..273bf3191 100644 --- a/test/e2e/e2e_local_source_upload_test.go +++ b/test/e2e/v1alpha1/e2e_local_source_upload_test.go @@ -13,7 +13,7 @@ import ( "k8s.io/apimachinery/pkg/types" buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test/utils" + utils "github.com/shipwright-io/build/test/utils/v1alpha1" ) var _ = Describe("For a Kubernetes cluster with Tekton and build installed", func() { @@ -41,7 +41,7 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildah_cr_local_source_upload.yaml", + "test/data/v1alpha1/build_buildah_cr_local_source_upload.yaml", ) }) @@ -50,7 +50,7 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun buildRun, err = buildRunTestData( testBuild.Namespace, testID, - "test/data/buildrun_buildah_cr_local_source_upload.yaml", + "test/data/v1alpha1/buildrun_buildah_cr_local_source_upload.yaml", ) Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") diff --git a/test/e2e/e2e_one_off_builds_test.go b/test/e2e/v1alpha1/e2e_one_off_builds_test.go similarity index 100% rename from test/e2e/e2e_one_off_builds_test.go rename to test/e2e/v1alpha1/e2e_one_off_builds_test.go diff --git a/test/e2e/e2e_params_test.go b/test/e2e/v1alpha1/e2e_params_test.go similarity index 96% rename from test/e2e/e2e_params_test.go rename to test/e2e/v1alpha1/e2e_params_test.go index 96c290028..608b5adf2 100644 --- a/test/e2e/e2e_params_test.go +++ b/test/e2e/v1alpha1/e2e_params_test.go @@ -9,10 +9,10 @@ import ( . "github.com/onsi/gomega" buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test" + test "github.com/shipwright-io/build/test/v1alpha1_samples" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) var _ = Describe("For a Kubernetes cluster with Tekton and build installed", func() { @@ -101,7 +101,7 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun OutputImage("dummy"). // The parameters StringParamValue("env1", "13"). - StringParamValueFromConfigMap("env2", "a-configmap", "number1", pointer.String("2${CONFIGMAP_VALUE}")). + StringParamValueFromConfigMap("env2", "a-configmap", "number1", ptr.To[string]("2${CONFIGMAP_VALUE}")). ArrayParamValueFromConfigMap("commands", "a-configmap", "shell", nil). ArrayParamValue("commands", "-c"). Create() @@ -115,7 +115,7 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun GenerateServiceAccount(). StringParamValue("image", "registry.access.redhat.com/ubi9/ubi-minimal"). StringParamValueFromSecret("env3", "a-secret", "number2", nil). - ArrayParamValueFromSecret("args", "a-secret", "number3", pointer.String("${SECRET_VALUE}9")). + ArrayParamValueFromSecret("args", "a-secret", "number3", ptr.To[string]("${SECRET_VALUE}9")). ArrayParamValue("args", "47"). Create() Expect(err).ToNot(HaveOccurred()) diff --git a/test/e2e/e2e_rbac_test.go b/test/e2e/v1alpha1/e2e_rbac_test.go similarity index 100% rename from test/e2e/e2e_rbac_test.go rename to test/e2e/v1alpha1/e2e_rbac_test.go diff --git a/test/e2e/v1alpha1/e2e_suite_test.go b/test/e2e/v1alpha1/e2e_suite_test.go new file mode 100644 index 000000000..a3a186ee6 --- /dev/null +++ b/test/e2e/v1alpha1/e2e_suite_test.go @@ -0,0 +1,52 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "os" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + utils "github.com/shipwright-io/build/test/utils/v1alpha1" +) + +var ( + testBuild *utils.TestBuild + stop = make(chan struct{}) +) + +func TestE2E(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "E2E Suite") +} + +var ( + _ = BeforeSuite(func() { + var ( + ok bool + err error + ) + + testBuild, err = utils.NewTestBuild() + Expect(err).ToNot(HaveOccurred()) + + testBuild.Namespace, ok = os.LookupEnv("TEST_NAMESPACE") + Expect(ok).To(BeTrue(), "TEST_NAMESPACE should be set") + Expect(testBuild.Namespace).ToNot(BeEmpty()) + + // create the pipeline service account + Logf("Creating the pipeline service account") + createPipelineServiceAccount(testBuild) + + // create the container registry secret + Logf("Creating the container registry secret") + createContainerRegistrySecret(testBuild) + }) + + _ = AfterSuite(func() { + close(stop) + }) +) diff --git a/test/e2e/e2e_test.go b/test/e2e/v1alpha1/e2e_test.go similarity index 83% rename from test/e2e/e2e_test.go rename to test/e2e/v1alpha1/e2e_test.go index 9d234d5db..98ed07c4c 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/v1alpha1/e2e_test.go @@ -55,12 +55,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_buildah_shipwright_managed_push_cr.yaml", + "samples/v1alpha1/build/build_buildah_shipwright_managed_push_cr.yaml", ) }) It("successfully runs a build and surface results to BuildRun", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildah_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_buildah_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") appendRegistryInsecureParamValue(build, buildRun) @@ -79,12 +79,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_buildah_strategy_managed_push_cr.yaml", + "samples/v1alpha1/build/build_buildah_strategy_managed_push_cr.yaml", ) }) It("successfully runs a build and surface results to BuildRun", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildah_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_buildah_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") appendRegistryInsecureParamValue(build, buildRun) @@ -103,12 +103,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildah_cr_custom_context+dockerfile.yaml", + "test/data/v1alpha1/build_buildah_cr_custom_context+dockerfile.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/buildrun_buildah_cr_custom_context+dockerfile.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1alpha1/buildrun_buildah_cr_custom_context+dockerfile.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") appendRegistryInsecureParamValue(build, buildRun) @@ -126,12 +126,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_buildpacks-v3-heroku_cr.yaml", + "samples/v1alpha1/build/build_buildpacks-v3-heroku_cr.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildpacks-v3-heroku_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_buildpacks-v3-heroku_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -145,7 +145,7 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun BeforeEach(func() { testID = generateTestID("buildpacks-v3-heroku-namespaced") - buildStrategy, err = buildStrategyTestData(testBuild.Namespace, "samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml") + buildStrategy, err = buildStrategyTestData(testBuild.Namespace, "samples/v1alpha1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml") Expect(err).ToNot(HaveOccurred()) err = testBuild.CreateBuildStrategy(buildStrategy) @@ -155,12 +155,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_buildpacks-v3-heroku_namespaced_cr.yaml", + "samples/v1alpha1/build/build_buildpacks-v3-heroku_namespaced_cr.yaml", ) }) It("successfully runs a build and surface results to BuildRun", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml") Expect(err).ToNot(HaveOccurred()) buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -183,12 +183,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_buildpacks-v3_cr.yaml", + "samples/v1alpha1/build/build_buildpacks-v3_cr.yaml", ) }) It("successfully runs a build and surface results to BuildRun", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildpacks-v3_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_buildpacks-v3_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -203,7 +203,7 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun BeforeEach(func() { testID = generateTestID("buildpacks-v3-namespaced") - buildStrategy, err = buildStrategyTestData(testBuild.Namespace, "samples/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml") + buildStrategy, err = buildStrategyTestData(testBuild.Namespace, "samples/v1alpha1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml") Expect(err).ToNot(HaveOccurred()) err = testBuild.CreateBuildStrategy(buildStrategy) @@ -213,12 +213,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_buildpacks-v3_namespaced_cr.yaml", + "samples/v1alpha1/build/build_buildpacks-v3_namespaced_cr.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildpacks-v3_namespaced_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_buildpacks-v3_namespaced_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -240,12 +240,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildpacks-v3_php_cr.yaml", + "test/data/v1alpha1/build_buildpacks-v3_php_cr.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/buildrun_buildpacks-v3_php_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1alpha1/buildrun_buildpacks-v3_php_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -262,12 +262,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildpacks-v3_ruby_cr.yaml", + "test/data/v1alpha1/build_buildpacks-v3_ruby_cr.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/buildrun_buildpacks-v3_ruby_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1alpha1/buildrun_buildpacks-v3_ruby_cr.yaml") Expect(err).ToNot(HaveOccurred()) buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -284,12 +284,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildpacks-v3_golang_cr.yaml", + "test/data/v1alpha1/build_buildpacks-v3_golang_cr.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/buildrun_buildpacks-v3_golang_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1alpha1/buildrun_buildpacks-v3_golang_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -305,12 +305,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildpacks-v3_golang_cr_env.yaml", + "test/data/v1alpha1/build_buildpacks-v3_golang_cr_env.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/buildrun_buildpacks-v3_golang_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1alpha1/buildrun_buildpacks-v3_golang_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -327,13 +327,13 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildpacks-v3_golang_delete_cr.yaml", + "test/data/v1alpha1/build_buildpacks-v3_golang_delete_cr.yaml", ) }) It("successfully deletes the BuildRun after the Build is deleted", func() { By("running a build and expecting it to succeed") - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/buildrun_buildpacks-v3_golang_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1alpha1/buildrun_buildpacks-v3_golang_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -364,12 +364,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildpacks-v3_java_cr.yaml", + "test/data/v1alpha1/build_buildpacks-v3_java_cr.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/buildrun_buildpacks-v3_java_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1alpha1/buildrun_buildpacks-v3_java_cr.yaml") Expect(err).ToNot(HaveOccurred()) buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -386,12 +386,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_kaniko_cr.yaml", + "samples/v1alpha1/build/build_kaniko_cr.yaml", ) }) It("successfully runs a build and surface results to BuildRun", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_kaniko_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_kaniko_cr.yaml") Expect(err).ToNot(HaveOccurred()) buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -409,12 +409,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_kaniko_cr_advanced_dockerfile.yaml", + "test/data/v1alpha1/build_kaniko_cr_advanced_dockerfile.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/buildrun_kaniko_cr_advanced_dockerfile.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1alpha1/buildrun_kaniko_cr_advanced_dockerfile.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -431,12 +431,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_kaniko_cr_custom_context+dockerfile.yaml", + "test/data/v1alpha1/build_kaniko_cr_custom_context+dockerfile.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/buildrun_kaniko_cr_custom_context+dockerfile.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1alpha1/buildrun_kaniko_cr_custom_context+dockerfile.yaml") Expect(err).ToNot(HaveOccurred()) buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -453,12 +453,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_kaniko-trivy-good_cr.yaml", + "samples/v1alpha1/build/build_kaniko-trivy-good_cr.yaml", ) }) It("successfully runs a build and surface results to BuildRun", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_kaniko-trivy-good_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_kaniko-trivy-good_cr.yaml") Expect(err).ToNot(HaveOccurred()) buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -476,12 +476,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_kaniko-trivy-bad_cr.yaml", + "samples/v1alpha1/build/build_kaniko-trivy-bad_cr.yaml", ) }) It("fails to run a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_kaniko-trivy-bad_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_kaniko-trivy-bad_cr.yaml") Expect(err).ToNot(HaveOccurred()) validateBuildRunToFail(testBuild, buildRun) @@ -497,12 +497,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_buildkit_cr.yaml", + "samples/v1alpha1/build/build_buildkit_cr.yaml", ) }) It("successfully runs a build and surface results to BuildRun", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildkit_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_buildkit_cr.yaml") Expect(err).ToNot(HaveOccurred()) buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -529,12 +529,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "samples/build/build_source-to-image_cr.yaml", + "samples/v1alpha1/build/build_source-to-image_cr.yaml", ) }) It("successfully runs a build and surface results to BuildRun", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_source-to-image_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_source-to-image_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -560,12 +560,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildah_cr_private_github.yaml", + "test/data/v1alpha1/build_buildah_cr_private_github.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildah_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_buildah_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -582,12 +582,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_buildah_cr_private_gitlab.yaml", + "test/data/v1alpha1/build_buildah_cr_private_gitlab.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_buildah_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_buildah_cr.yaml") Expect(err).ToNot(HaveOccurred()) buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -604,12 +604,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_kaniko_cr_private_github.yaml", + "test/data/v1alpha1/build_kaniko_cr_private_github.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_kaniko_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_kaniko_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -626,12 +626,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_kaniko_cr_private_gitlab.yaml", + "test/data/v1alpha1/build_kaniko_cr_private_gitlab.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_kaniko_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_kaniko_cr.yaml") Expect(err).ToNot(HaveOccurred()) buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -648,12 +648,12 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_source-to-image_cr_private_github.yaml", + "test/data/v1alpha1/build_source-to-image_cr_private_github.yaml", ) }) It("successfully runs a build", func() { - buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/buildrun/buildrun_source-to-image_cr.yaml") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1alpha1/buildrun/buildrun_source-to-image_cr.yaml") Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") buildRun = validateBuildRunToSucceed(testBuild, buildRun) @@ -669,10 +669,10 @@ var _ = Describe("For a Kubernetes cluster with Tekton and build installed", fun build = createBuild( testBuild, testID, - "test/data/build_non_existing_repo.yaml", + "test/data/v1alpha1/build_non_existing_repo.yaml", ) - buildRun, err = buildRunTestData(build.Namespace, testID, "test/data/buildrun_non_existing_repo.yaml") + buildRun, err = buildRunTestData(build.Namespace, testID, "test/data/v1alpha1/buildrun_non_existing_repo.yaml") Expect(err).ToNot(HaveOccurred()) validateBuildRunToFail(testBuild, buildRun) diff --git a/test/e2e/validators_test.go b/test/e2e/v1alpha1/validators_test.go similarity index 98% rename from test/e2e/validators_test.go rename to test/e2e/v1alpha1/validators_test.go index fb48b9a16..ba68b0a58 100644 --- a/test/e2e/validators_test.go +++ b/test/e2e/v1alpha1/validators_test.go @@ -20,11 +20,11 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/kubectl/pkg/scheme" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "github.com/shipwright-io/build/pkg/apis" buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test/utils" + utils "github.com/shipwright-io/build/test/utils/v1alpha1" ) const ( @@ -280,7 +280,7 @@ func readAndDecode(filePath string) (runtime.Object, error) { return nil, err } - payload, err := os.ReadFile(filepath.Join("..", "..", filePath)) + payload, err := os.ReadFile(filepath.Join("..", "..", "..", filePath)) if err != nil { return nil, err } @@ -337,7 +337,7 @@ func buildRunTestData(ns string, identifier string, filePath string) (*buildv1al serviceAccountName := os.Getenv(EnvVarServiceAccountName) if serviceAccountName == "generated" { buildRun.Spec.ServiceAccount = &buildv1alpha1.ServiceAccount{ - Generate: pointer.Bool(true), + Generate: ptr.To[bool](true), } } else { buildRun.Spec.ServiceAccount = &buildv1alpha1.ServiceAccount{ diff --git a/test/e2e/v1beta1/common_suite_test.go b/test/e2e/v1beta1/common_suite_test.go new file mode 100644 index 000000000..21b5fbc52 --- /dev/null +++ b/test/e2e/v1beta1/common_suite_test.go @@ -0,0 +1,447 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "context" + "fmt" + "os" + "strconv" + "strings" + "time" + + . "github.com/onsi/ginkgo/v2" + + core "k8s.io/api/core/v1" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/utils/ptr" + + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +const ( + pollCreateInterval = 1 * time.Second + pollCreateTimeout = 10 * time.Second +) + +type buildPrototype struct{ build buildv1beta1.Build } +type buildRunPrototype struct{ buildRun buildv1beta1.BuildRun } + +func NewBuildPrototype() *buildPrototype { + return &buildPrototype{ + build: buildv1beta1.Build{}, + } +} + +func (b *buildPrototype) Name(name string) *buildPrototype { + b.build.ObjectMeta.Name = name + return b +} + +func (b *buildPrototype) Namespace(namespace string) *buildPrototype { + b.build.ObjectMeta.Namespace = namespace + return b +} + +func (b *buildPrototype) BuildStrategy(name string) *buildPrototype { + var bs = buildv1beta1.NamespacedBuildStrategyKind + b.build.Spec.Strategy = buildv1beta1.Strategy{ + Kind: &bs, + Name: name, + } + return b +} + +func (b *buildPrototype) ClusterBuildStrategy(name string) *buildPrototype { + var cbs = buildv1beta1.ClusterBuildStrategyKind + b.build.Spec.Strategy = buildv1beta1.Strategy{ + Kind: &cbs, + Name: name, + } + return b +} + +func (b *buildPrototype) SourceCredentials(name string) *buildPrototype { + if name != "" { + if b.build.Spec.Source.GitSource == nil { + b.build.Spec.Source.GitSource = &buildv1beta1.Git{} + } + b.build.Spec.Source.GitSource.CloneSecret = &name + } + + return b +} + +func (b *buildPrototype) SourceGit(repository string) *buildPrototype { + if b.build.Spec.Source.GitSource == nil { + b.build.Spec.Source.GitSource = &buildv1beta1.Git{} + } + b.build.Spec.Source.GitSource.URL = ptr.To[string](repository) + b.build.Spec.Source.OCIArtifact = nil + return b +} + +func (b *buildPrototype) SourceBundle(image string) *buildPrototype { + if b.build.Spec.Source.OCIArtifact == nil { + b.build.Spec.Source.OCIArtifact = &buildv1beta1.OCIArtifact{} + } + b.build.Spec.Source.Type = buildv1beta1.OCIArtifactType + b.build.Spec.Source.OCIArtifact.Image = image + return b +} + +func (b *buildPrototype) SourceBundlePrune(prune buildv1beta1.PruneOption) *buildPrototype { + if b.build.Spec.Source.OCIArtifact == nil { + b.build.Spec.Source.OCIArtifact = &buildv1beta1.OCIArtifact{} + } + b.build.Spec.Source.OCIArtifact.Prune = &prune + return b +} + +func (b *buildPrototype) SourceContextDir(contextDir string) *buildPrototype { + b.build.Spec.Source.ContextDir = ptr.To[string](contextDir) + return b +} + +func (b *buildPrototype) Dockerfile(dockerfile string) *buildPrototype { + b.build.Spec.ParamValues = append( + b.build.Spec.ParamValues, + buildv1beta1.ParamValue{ + Name: "dockerfile", + SingleValue: &buildv1beta1.SingleValue{ + Value: &dockerfile, + }, + }, + ) + return b +} + +func (b *buildPrototype) OutputImage(image string) *buildPrototype { + b.build.Spec.Output.Image = image + return b +} + +func (b *buildPrototype) determineParameterIndex(name string) int { + index := -1 + for i, paramValue := range b.build.Spec.ParamValues { + if paramValue.Name == name { + index = i + break + } + } + + if index == -1 { + index = len(b.build.Spec.ParamValues) + b.build.Spec.ParamValues = append(b.build.Spec.ParamValues, buildv1beta1.ParamValue{ + Name: name, + }) + } + + return index +} + +// ArrayParamValue adds an item to an array parameter, if the parameter is not yet present, it is being added +func (b *buildPrototype) ArrayParamValue(name string, value string) *buildPrototype { + index := b.determineParameterIndex(name) + b.build.Spec.ParamValues[index].Values = append(b.build.Spec.ParamValues[index].Values, buildv1beta1.SingleValue{ + Value: &value, + }) + + return b +} + +// ArrayParamValueFromConfigMap adds an item to an array parameter, if the parameter is not yet present, it is being added +func (b *buildPrototype) ArrayParamValueFromConfigMap(name string, configMapName string, configMapKey string, format *string) *buildPrototype { + index := b.determineParameterIndex(name) + b.build.Spec.ParamValues[index].Values = append(b.build.Spec.ParamValues[index].Values, buildv1beta1.SingleValue{ + ConfigMapValue: &buildv1beta1.ObjectKeyRef{ + Name: configMapName, + Key: configMapKey, + Format: format, + }, + }) + + return b +} + +// ArrayParamValueFromSecret adds an item to an array parameter, if the parameter is not yet present, it is being added +func (b *buildPrototype) ArrayParamValueFromSecret(name string, secretName string, secretKey string, format *string) *buildPrototype { + index := b.determineParameterIndex(name) + b.build.Spec.ParamValues[index].Values = append(b.build.Spec.ParamValues[index].Values, buildv1beta1.SingleValue{ + SecretValue: &buildv1beta1.ObjectKeyRef{ + Name: secretName, + Key: secretKey, + Format: format, + }, + }) + + return b +} + +func (b *buildPrototype) StringParamValue(name string, value string) *buildPrototype { + b.build.Spec.ParamValues = append(b.build.Spec.ParamValues, buildv1beta1.ParamValue{ + Name: name, + SingleValue: &buildv1beta1.SingleValue{ + Value: &value, + }, + }) + + return b +} + +func (b *buildPrototype) StringParamValueFromConfigMap(name string, configMapName string, configMapKey string, format *string) *buildPrototype { + b.build.Spec.ParamValues = append(b.build.Spec.ParamValues, buildv1beta1.ParamValue{ + Name: name, + SingleValue: &buildv1beta1.SingleValue{ + ConfigMapValue: &buildv1beta1.ObjectKeyRef{ + Name: configMapName, + Key: configMapKey, + Format: format, + }, + }, + }) + + return b +} + +func (b *buildPrototype) StringParamValueFromSecret(name string, secretName string, secretKey string, format *string) *buildPrototype { + b.build.Spec.ParamValues = append(b.build.Spec.ParamValues, buildv1beta1.ParamValue{ + Name: name, + SingleValue: &buildv1beta1.SingleValue{ + SecretValue: &buildv1beta1.ObjectKeyRef{ + Name: secretName, + Key: secretKey, + Format: format, + }, + }, + }) + + return b +} + +func (b *buildPrototype) OutputImageCredentials(name string) *buildPrototype { + if name != "" { + b.build.Spec.Output.PushSecret = &name + } + return b +} + +func (b *buildPrototype) OutputImageInsecure(insecure bool) *buildPrototype { + b.build.Spec.Output.Insecure = &insecure + + return b +} + +func (b buildPrototype) Create() (build *buildv1beta1.Build, err error) { + ctx := context.Background() + + _, err = testBuild. + BuildClientSet. + ShipwrightV1beta1(). + Builds(b.build.Namespace). + Create(ctx, &b.build, meta.CreateOptions{}) + + if err != nil { + return nil, err + } + + err = wait.PollImmediate(pollCreateInterval, pollCreateTimeout, func() (done bool, err error) { + build, err = testBuild.BuildClientSet.ShipwrightV1beta1().Builds(b.build.Namespace).Get(ctx, b.build.Name, meta.GetOptions{}) + if err != nil { + return false, err + } + + return build.Status.Registered != nil && *build.Status.Registered == core.ConditionTrue, nil + }) + + return +} + +// BuildSpec returns the BuildSpec of this Build (no cluster resource is created) +func (b buildPrototype) BuildSpec() (build *buildv1beta1.BuildSpec) { + return &b.build.Spec +} + +func NewBuildRunPrototype() *buildRunPrototype { + return &buildRunPrototype{buildRun: buildv1beta1.BuildRun{}} +} + +func (b *buildRunPrototype) Name(name string) *buildRunPrototype { + b.buildRun.ObjectMeta.Name = name + return b +} + +func (b *buildRunPrototype) Namespace(namespace string) *buildRunPrototype { + b.buildRun.Namespace = namespace + return b +} + +func (b *buildRunPrototype) ForBuild(build *buildv1beta1.Build) *buildRunPrototype { + if b.buildRun.Spec.Build == nil { + b.buildRun.Spec.Build = &buildv1beta1.ReferencedBuild{} + } + b.buildRun.Spec.Build.Name = build.Name + b.buildRun.ObjectMeta.Namespace = build.Namespace + return b +} + +func (b *buildRunPrototype) WithBuildSpec(buildSpec *buildv1beta1.BuildSpec) *buildRunPrototype { + if b.buildRun.Spec.Build == nil { + b.buildRun.Spec.Build = &buildv1beta1.ReferencedBuild{} + } + b.buildRun.Spec.Build.Build = buildSpec + return b +} + +func (b *buildRunPrototype) GenerateServiceAccount() *buildRunPrototype { + generate := ".generate" + if b.buildRun.Spec.ServiceAccount == nil { + b.buildRun.Spec.ServiceAccount = &generate + } + return b +} + +func (b *buildRunPrototype) determineParameterIndex(name string) int { + index := -1 + for i, paramValue := range b.buildRun.Spec.ParamValues { + if paramValue.Name == name { + index = i + break + } + } + + if index == -1 { + index = len(b.buildRun.Spec.ParamValues) + b.buildRun.Spec.ParamValues = append(b.buildRun.Spec.ParamValues, buildv1beta1.ParamValue{ + Name: name, + }) + } + + return index +} + +// ArrayParamValue adds an item to an array parameter, if the parameter is not yet present, it is being added +func (b *buildRunPrototype) ArrayParamValue(name string, value string) *buildRunPrototype { + index := b.determineParameterIndex(name) + b.buildRun.Spec.ParamValues[index].Values = append(b.buildRun.Spec.ParamValues[index].Values, buildv1beta1.SingleValue{ + Value: &value, + }) + + return b +} + +// ArrayParamValueFromConfigMap adds an item to an array parameter, if the parameter is not yet present, it is being added +func (b *buildRunPrototype) ArrayParamValueFromConfigMap(name string, configMapName string, configMapKey string, format *string) *buildRunPrototype { + index := b.determineParameterIndex(name) + b.buildRun.Spec.ParamValues[index].Values = append(b.buildRun.Spec.ParamValues[index].Values, buildv1beta1.SingleValue{ + ConfigMapValue: &buildv1beta1.ObjectKeyRef{ + Name: configMapName, + Key: configMapKey, + Format: format, + }, + }) + + return b +} + +// ArrayParamValueFromSecret adds an item to an array parameter, if the parameter is not yet present, it is being added +func (b *buildRunPrototype) ArrayParamValueFromSecret(name string, secretName string, secretKey string, format *string) *buildRunPrototype { + index := b.determineParameterIndex(name) + b.buildRun.Spec.ParamValues[index].Values = append(b.buildRun.Spec.ParamValues[index].Values, buildv1beta1.SingleValue{ + SecretValue: &buildv1beta1.ObjectKeyRef{ + Name: secretName, + Key: secretKey, + Format: format, + }, + }) + + return b +} + +func (b *buildRunPrototype) StringParamValue(name string, value string) *buildRunPrototype { + b.buildRun.Spec.ParamValues = append(b.buildRun.Spec.ParamValues, buildv1beta1.ParamValue{ + Name: name, + SingleValue: &buildv1beta1.SingleValue{ + Value: &value, + }, + }) + + return b +} + +func (b *buildRunPrototype) StringParamValueFromConfigMap(name string, configMapName string, configMapKey string, format *string) *buildRunPrototype { + b.buildRun.Spec.ParamValues = append(b.buildRun.Spec.ParamValues, buildv1beta1.ParamValue{ + Name: name, + SingleValue: &buildv1beta1.SingleValue{ + ConfigMapValue: &buildv1beta1.ObjectKeyRef{ + Name: configMapName, + Key: configMapKey, + Format: format, + }, + }, + }) + + return b +} + +func (b *buildRunPrototype) StringParamValueFromSecret(name string, secretName string, secretKey string, format *string) *buildRunPrototype { + b.buildRun.Spec.ParamValues = append(b.buildRun.Spec.ParamValues, buildv1beta1.ParamValue{ + Name: name, + SingleValue: &buildv1beta1.SingleValue{ + SecretValue: &buildv1beta1.ObjectKeyRef{ + Name: secretName, + Key: secretKey, + Format: format, + }, + }, + }) + + return b +} + +func (b *buildRunPrototype) Create() (*buildv1beta1.BuildRun, error) { + return testBuild. + BuildClientSet. + ShipwrightV1beta1(). + BuildRuns(b.buildRun.Namespace). + Create(context.Background(), &b.buildRun, meta.CreateOptions{}) +} + +// Logf logs data +func Logf(format string, args ...interface{}) { + currentTime := time.Now().UTC().Format(time.RFC3339) + + fmt.Fprintf( + GinkgoWriter, + fmt.Sprintf("%s %d %s\n", currentTime, getGinkgoNode(), format), + args..., + ) +} + +func getArg(argName string) (bool, string) { + for i, arg := range os.Args { + if arg == argName { + return true, os.Args[i+1] + } else if strings.HasPrefix(arg, argName+"=") { + argAndValue := strings.SplitN(arg, "=", 2) + return true, argAndValue[1] + } + } + return false, "" +} + +func getGinkgoNode() int { + defined, ginkgoNodeString := getArg("--ginkgo.parallel.node") + if !defined { + return 1 + } + ginkgoNode, err := strconv.Atoi(ginkgoNodeString) + if err != nil { + fmt.Printf("Error: %s", err.Error()) + return 0 + } + return ginkgoNode +} diff --git a/test/e2e/v1beta1/common_test.go b/test/e2e/v1beta1/common_test.go new file mode 100644 index 000000000..4ba907bb4 --- /dev/null +++ b/test/e2e/v1beta1/common_test.go @@ -0,0 +1,269 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "strconv" + "strings" + "time" + + . "github.com/onsi/gomega" + knativeapis "knative.dev/pkg/apis" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/rand" + + buildv1alpha1 "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" + "github.com/shipwright-io/build/pkg/reconciler/buildrun/resources" + utils "github.com/shipwright-io/build/test/utils/v1beta1" +) + +func generateTestID(id string) string { + return id + "-" + rand.String(5) +} + +func removeTestIDSuffix(id string) string { + return id[:len(id)-6] +} + +func createBuild(testBuild *utils.TestBuild, identifier string, filePath string) *buildv1beta1.Build { + build, err := buildTestData(testBuild.Namespace, identifier, filePath) + Expect(err).ToNot(HaveOccurred(), "Error retrieving build test data") + + amendBuild(identifier, build) + + // For Builds that use a namespaces build strategy, there is a race condition: we just created the + // build strategy and it might be that the build controller does not yet have this in his cache + // and therefore marks the build as not registered with reason BuildStrategyNotFound + Eventually(func() buildv1beta1.BuildReason { + // cleanup the build of the previous try + if build.Status.Registered != nil && *build.Status.Registered != "" { + err = testBuild.DeleteBuild(build.Name) + Expect(err).ToNot(HaveOccurred()) + + // create a fresh object + build, err = buildTestData(testBuild.Namespace, identifier, filePath) + Expect(err).ToNot(HaveOccurred(), "Error retrieving build test data") + + amendBuild(identifier, build) + } + + err = testBuild.CreateBuild(build) + Expect(err).ToNot(HaveOccurred(), "Unable to create build %s", identifier) + Logf("Build %s created", identifier) + + build, err = testBuild.GetBuildTillValidation(build.Name) + Expect(err).ToNot(HaveOccurred()) + + return *build.Status.Reason + }, time.Duration(10*time.Second), time.Second).Should(Equal(buildv1beta1.SucceedStatus)) + + return build +} + +// amendOutputImage amend container image URL based on informed image repository. +func amendOutputImage(b *buildv1beta1.Build, imageRepo string, insecure bool) { + if imageRepo == "" { + return + } + + // image tag is the build name without the test id suffix as this would pollute the container registry + imageTag := removeTestIDSuffix(b.Name) + + imageURL := fmt.Sprintf("%s:%s", imageRepo, imageTag) + b.Spec.Output.Image = imageURL + b.Spec.Output.Insecure = &insecure + + Logf("Amended object: name='%s', image-url='%s'", b.Name, imageURL) +} + +// amendOutputCredentials amend secret-ref for output image. +func amendOutputCredentials(b *buildv1beta1.Build, secretName string) { + if secretName == "" { + return + } + b.Spec.Output.PushSecret = &secretName + Logf("Amended object: name='%s', secret-ref='%s'", b.Name, secretName) +} + +// amendSourceSecretName patch Build source.Credentials with secret name. +func amendSourceSecretName(b *buildv1beta1.Build, secretName string) { + if secretName == "" { + return + } + b.Spec.Source.GitSource.CloneSecret = &secretName +} + +// amendSourceURL patch Build source.URL with informed string. +func amendSourceURL(b *buildv1beta1.Build, sourceURL string) { + if sourceURL == "" { + return + } + b.Spec.Source.GitSource.URL = &sourceURL +} + +// amendBuild make changes on build object. +func amendBuild(identifier string, b *buildv1beta1.Build) { + if strings.Contains(identifier, "github") { + amendSourceSecretName(b, os.Getenv(EnvVarSourceURLSecret)) + amendSourceURL(b, os.Getenv(EnvVarSourceURLGithub)) + } else if strings.Contains(identifier, "gitlab") { + amendSourceSecretName(b, os.Getenv(EnvVarSourceURLSecret)) + amendSourceURL(b, os.Getenv(EnvVarSourceURLGitlab)) + } + + insecure := false + value, found := os.LookupEnv(EnvVarImageRepoInsecure) + if found { + var err error + insecure, err = strconv.ParseBool(value) + Expect(err).ToNot(HaveOccurred()) + } + + amendOutputImage(b, os.Getenv(EnvVarImageRepo), insecure) + amendOutputCredentials(b, os.Getenv(EnvVarImageRepoSecret)) +} + +// retrieveBuildAndBuildRun will retrieve the build and buildRun +func retrieveBuildAndBuildRun(testBuild *utils.TestBuild, namespace string, buildRunName string) (*buildv1beta1.Build, *buildv1beta1.BuildRun, error) { + buildRun, err := testBuild.LookupBuildRun(types.NamespacedName{Name: buildRunName, Namespace: namespace}) + if err != nil { + Logf("Failed to get BuildRun %q: %s", buildRunName, err) + return nil, nil, err + } + + var alphaBuild buildv1alpha1.Build + var obj unstructured.Unstructured + + buildRun.ConvertTo(testBuild.Context, &obj) + jsonData, err := json.Marshal(obj.Object) + if err != nil { + Logf("Failed to convert the buildRun to v1alpha1: %s", err) + } + + var alphaBuildRun buildv1alpha1.BuildRun + json.Unmarshal(jsonData, &alphaBuildRun) + + if err := resources.GetBuildObject(testBuild.Context, testBuild.ControllerRuntimeClient, &alphaBuildRun, &alphaBuild); err != nil { + Logf("Failed to get Build from BuildRun %s: %s", buildRunName, err) + return nil, buildRun, err + } + + alphaBuild.ConvertTo(testBuild.Context, &obj) + jsonData, err = json.Marshal(obj.Object) + if err != nil { + Logf("Failed to convert the build to v1beta1: %s", err) + } + var betaBuild buildv1beta1.Build + json.Unmarshal(jsonData, &betaBuild) + + return &betaBuild, buildRun, nil +} + +// printTestFailureDebugInfo will output the status of Build, BuildRun, TaskRun and Pod, also print logs of Pod +func printTestFailureDebugInfo(testBuild *utils.TestBuild, namespace string, buildRunName string) { + Logf("Print failed BuildRun's log") + + build, buildRun, err := retrieveBuildAndBuildRun(testBuild, namespace, buildRunName) + if err != nil { + Logf("Failed to retrieve build and buildrun logs: %v", err) + } + + if build != nil { + Logf("The status of Build %s: registered=%s, reason=%s", build.Name, *build.Status.Registered, *build.Status.Reason) + if buildJSON, err := json.Marshal(build); err == nil { + Logf("The full Build: %s", string(buildJSON)) + } + } + + if buildRun != nil { + brCondition := buildRun.Status.GetCondition(buildv1beta1.Succeeded) + if brCondition != nil { + Logf("The status of BuildRun %s: status=%s, reason=%s", buildRun.Name, brCondition.Status, brCondition.Reason) + } + if buildRunJSON, err := json.Marshal(buildRun); err == nil { + Logf("The full BuildRun: %s", string(buildRunJSON)) + } + + podName := "" + + // Only log details of TaskRun if Tekton objects can be accessed + if os.Getenv(EnvVarVerifyTektonObjects) == "true" { + if taskRun, _ := testBuild.LookupTaskRunUsingBuildRun(buildRun); taskRun != nil { + condition := taskRun.Status.GetCondition(knativeapis.ConditionSucceeded) + if condition != nil { + Logf("The status of TaskRun %s: reason=%s, message=%s", taskRun.Name, condition.Reason, condition.Message) + } + + if taskRunJSON, err := json.Marshal(taskRun); err == nil { + Logf("The full TaskRun: %s", string(taskRunJSON)) + } + + podName = taskRun.Status.PodName + } + } + + // retrieve or query pod depending on whether we have the pod name from the TaskRun + var pod *corev1.Pod + if podName != "" { + pod, err = testBuild.LookupPod(types.NamespacedName{Name: podName, Namespace: namespace}) + if err != nil { + Logf("Error retrieving pod %s: %v", podName, err) + pod = nil + } + } else { + podList, err := testBuild.Clientset.CoreV1().Pods(namespace).List(testBuild.Context, metav1.ListOptions{ + LabelSelector: labels.FormatLabels(map[string]string{ + buildv1beta1.LabelBuildRun: buildRunName, + }), + }) + + if err == nil && len(podList.Items) > 0 { + pod = &podList.Items[0] + } + } + + if pod != nil { + Logf("The status of Pod %s: phase=%s, reason=%s, message=%s", pod.Name, pod.Status.Phase, pod.Status.Reason, pod.Status.Message) + if podJSON, err := json.Marshal(pod); err == nil { + Logf("The full Pod: %s", string(podJSON)) + } + + // Loop through the containers to print their logs + for _, container := range pod.Spec.Containers { + req := testBuild.Clientset.CoreV1().Pods(namespace).GetLogs(pod.Name, &corev1.PodLogOptions{ + TypeMeta: metav1.TypeMeta{}, + Container: container.Name, + Follow: false, + }) + + podLogs, err := req.Stream(testBuild.Context) + if err != nil { + Logf("Failed to retrieve the logs of container %s: %v", container.Name, err) + continue + } + + buf := new(bytes.Buffer) + _, err = io.Copy(buf, podLogs) + if err != nil { + Logf("Failed to copy logs of container %s to buffer: %v", container.Name, err) + continue + } + + Logf("Logs of container %s: %s", container.Name, buf.String()) + } + } + } +} diff --git a/test/e2e/v1beta1/e2e_image_mutate_test.go b/test/e2e/v1beta1/e2e_image_mutate_test.go new file mode 100644 index 000000000..965a58f1a --- /dev/null +++ b/test/e2e/v1beta1/e2e_image_mutate_test.go @@ -0,0 +1,89 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + containerreg "github.com/google/go-containerregistry/pkg/v1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +var _ = Describe("For a Kubernetes cluster with Tekton and build installed", func() { + var ( + err error + testID string + build *buildv1beta1.Build + buildRun *buildv1beta1.BuildRun + ) + + AfterEach(func() { + if CurrentSpecReport().Failed() { + printTestFailureDebugInfo(testBuild, testBuild.Namespace, testID) + } else if buildRun != nil { + validateServiceAccountDeletion(buildRun, testBuild.Namespace) + } + + if buildRun != nil { + testBuild.DeleteBR(buildRun.Name) + buildRun = nil + } + + if build != nil { + testBuild.DeleteBuild(build.Name) + build = nil + } + }) + + Context("when a Buildah build with label and annotation is defined", func() { + BeforeEach(func() { + testID = generateTestID("buildah-mutate") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_buildah_cr_mutate.yaml", + ) + }) + + It("should mutate an image with annotation and label", func() { + buildRun, err = buildRunTestData( + testBuild.Namespace, testID, + "test/data/v1beta1/buildrun_buildah_cr_mutate.yaml", + ) + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + appendRegistryInsecureParamValue(build, buildRun) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + + image := testBuild.GetImage(buildRun) + + Expect( + getImageAnnotation(image, "org.opencontainers.image.url"), + ).To(Equal("https://my-company.com/images")) + + Expect( + getImageLabel(image, "maintainer"), + ).To(Equal("team@my-company.com")) + }) + }) +}) + +func getImageAnnotation(img containerreg.Image, annotation string) string { + manifest, err := img.Manifest() + Expect(err).To(BeNil()) + + return manifest.Annotations[annotation] +} + +func getImageLabel(img containerreg.Image, label string) string { + config, err := img.ConfigFile() + Expect(err).To(BeNil()) + + return config.Config.Labels[label] +} diff --git a/test/e2e/v1beta1/e2e_ociartifact_test.go b/test/e2e/v1beta1/e2e_ociartifact_test.go new file mode 100644 index 000000000..c3dd381c9 --- /dev/null +++ b/test/e2e/v1beta1/e2e_ociartifact_test.go @@ -0,0 +1,276 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "bytes" + "fmt" + "os" + "strconv" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/docker/cli/cli/config" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +var _ = Describe("Test local source code (bundle) functionality", func() { + + insecure := false + value, found := os.LookupEnv(EnvVarImageRepoInsecure) + if found { + var err error + insecure, err = strconv.ParseBool(value) + Expect(err).ToNot(HaveOccurred()) + } + + var ( + testID string + err error + + build *buildv1beta1.Build + buildRun *buildv1beta1.BuildRun + ) + + AfterEach(func() { + if CurrentSpecReport().Failed() { + printTestFailureDebugInfo(testBuild, testBuild.Namespace, testID) + } else if buildRun != nil { + validateServiceAccountDeletion(buildRun, testBuild.Namespace) + } + + if buildRun != nil { + testBuild.DeleteBR(buildRun.Name) + buildRun = nil + } + + if build != nil { + testBuild.DeleteBuild(build.Name) + build = nil + } + }) + + Context("when using local source code bundle images as input", func() { + var inputImage, outputImage string + + BeforeEach(func() { + testID = generateTestID("bundle") + + inputImage = "ghcr.io/shipwright-io/sample-go/source-bundle:latest" + outputImage = fmt.Sprintf("%s/%s:%s", + os.Getenv(EnvVarImageRepo), + testID, + "latest", + ) + }) + + It("should work with Kaniko build strategy", func() { + build, err = NewBuildPrototype(). + ClusterBuildStrategy("kaniko"). + Name(testID). + Namespace(testBuild.Namespace). + SourceBundle(inputImage). + SourceContextDir("docker-build"). + Dockerfile("Dockerfile"). + OutputImage(outputImage). + OutputImageCredentials(os.Getenv(EnvVarImageRepoSecret)). + OutputImageInsecure(insecure). + Create() + Expect(err).ToNot(HaveOccurred()) + + buildRun, err = NewBuildRunPrototype(). + Name(testID). + ForBuild(build). + GenerateServiceAccount(). + Create() + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromBundleSource(buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + + It("should work with Buildpacks build strategy", func() { + build, err = NewBuildPrototype(). + ClusterBuildStrategy("buildpacks-v3"). + Name(testID). + Namespace(testBuild.Namespace). + SourceBundle(inputImage). + SourceContextDir("source-build"). + OutputImage(outputImage). + OutputImageCredentials(os.Getenv(EnvVarImageRepoSecret)). + OutputImageInsecure(insecure). + Create() + Expect(err).ToNot(HaveOccurred()) + + buildRun, err = NewBuildRunPrototype(). + Name(testID). + ForBuild(build). + GenerateServiceAccount(). + Create() + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromBundleSource(buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + + It("should work with Buildah build strategy", func() { + buildPrototype := NewBuildPrototype(). + ClusterBuildStrategy("buildah-shipwright-managed-push"). + Name(testID). + Namespace(testBuild.Namespace). + SourceBundle(inputImage). + SourceContextDir("docker-build"). + Dockerfile("Dockerfile"). + OutputImage(outputImage). + OutputImageCredentials(os.Getenv(EnvVarImageRepoSecret)). + OutputImageInsecure(insecure) + + if strings.Contains(outputImage, "cluster.local") { + parts := strings.Split(outputImage, "/") + host := parts[0] + buildPrototype.ArrayParamValue("registries-insecure", host) + } + + build, err = buildPrototype.Create() + Expect(err).ToNot(HaveOccurred()) + + buildRun, err = NewBuildRunPrototype(). + Name(testID). + ForBuild(build). + GenerateServiceAccount(). + Create() + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromBundleSource(buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + + It("should prune the source image after pulling it", func() { + var secretName = os.Getenv(EnvVarImageRepoSecret) + var registryName string + var auth authn.Authenticator + var tmpImage = fmt.Sprintf("%s/source-%s:%s", + os.Getenv(EnvVarImageRepo), + testID, + "latest", + ) + + By("looking up the registry name", func() { + ref, err := name.ParseReference(outputImage) + Expect(err).ToNot(HaveOccurred()) + + registryName = ref.Context().RegistryStr() + }) + + By("setting up the respective authenticator", func() { + switch { + case secretName != "": + secret, err := testBuild.Clientset. + CoreV1(). + Secrets(testBuild.Namespace). + Get(testBuild.Context, secretName, v1.GetOptions{}) + Expect(err).ToNot(HaveOccurred()) + + dockerConfigJSON, ok := secret.Data[".dockerconfigjson"] + Expect(ok).To(BeTrue()) + + configFile, err := config.LoadFromReader(bytes.NewReader(dockerConfigJSON)) + Expect(err).ToNot(HaveOccurred()) + + authConfig, err := configFile.GetAuthConfig(registryName) + Expect(err).ToNot(HaveOccurred()) + + auth = authn.FromConfig(authn.AuthConfig{ + Username: authConfig.Username, + Password: authConfig.Password, + Auth: authConfig.Auth, + IdentityToken: authConfig.IdentityToken, + RegistryToken: authConfig.RegistryToken, + }) + + default: + auth = authn.Anonymous + } + }) + + By("creating a temporary new input image based on the default input image", func() { + src, err := name.ParseReference(inputImage) + Expect(err).ToNot(HaveOccurred()) + + // Special case for a local registry in the cluster: + // Since the test client is not running in the cluster, it relies on being able to + // reach the same registry via a local port. Therefore, the image name needs to be + // different for the image copy preparation step. + var dstImage = tmpImage + if strings.Contains(dstImage, "cluster.local") { + dstImage = strings.ReplaceAll( + dstImage, + "registry.registry.svc.cluster.local", + "localhost", + ) + } + + dst, err := name.ParseReference(dstImage) + Expect(err).ToNot(HaveOccurred()) + + srcDesc, err := remote.Get(src) + Expect(err).ToNot(HaveOccurred()) + + image, err := srcDesc.Image() + Expect(err).ToNot(HaveOccurred()) + + Expect(remote.Write( + dst, + image, + remote.WithContext(testBuild.Context), + remote.WithAuth(auth), + )).ToNot(HaveOccurred()) + }) + + By("eventually running the actual build with prune option", func() { + build, err = NewBuildPrototype(). + ClusterBuildStrategy("kaniko"). + Name(testID). + Namespace(testBuild.Namespace). + SourceBundle(tmpImage). + SourceBundlePrune(buildv1beta1.PruneAfterPull). + SourceCredentials(secretName). + SourceContextDir("docker-build"). + Dockerfile("Dockerfile"). + OutputImage(outputImage). + OutputImageCredentials(secretName). + OutputImageInsecure(insecure). + Create() + Expect(err).ToNot(HaveOccurred()) + + buildRun, err = NewBuildRunPrototype(). + Name(testID). + ForBuild(build). + GenerateServiceAccount(). + Create() + Expect(err).ToNot(HaveOccurred()) + validateBuildRunToSucceed(testBuild, buildRun) + }) + + By("checking the temporary input image was removed", func() { + tmp, err := name.ParseReference(tmpImage) + Expect(err).ToNot(HaveOccurred()) + + _, err = remote.Head(tmp, remote.WithContext(testBuild.Context), remote.WithAuth(auth)) + Expect(err).To(HaveOccurred()) + }) + }) + }) +}) diff --git a/test/e2e/v1beta1/e2e_one_off_builds_test.go b/test/e2e/v1beta1/e2e_one_off_builds_test.go new file mode 100644 index 000000000..fc85ca21c --- /dev/null +++ b/test/e2e/v1beta1/e2e_one_off_builds_test.go @@ -0,0 +1,123 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "fmt" + "os" + "strconv" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/google/go-containerregistry/pkg/name" + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +var _ = Describe("Using One-Off Builds", func() { + + insecure := false + value, found := os.LookupEnv(EnvVarImageRepoInsecure) + if found { + var err error + insecure, err = strconv.ParseBool(value) + Expect(err).ToNot(HaveOccurred()) + } + + var ( + testID string + err error + + buildRun *buildv1beta1.BuildRun + ) + + AfterEach(func() { + if CurrentSpecReport().Failed() { + printTestFailureDebugInfo(testBuild, testBuild.Namespace, testID) + + } else if buildRun != nil { + validateServiceAccountDeletion(buildRun, testBuild.Namespace) + } + + if buildRun != nil { + testBuild.DeleteBR(buildRun.Name) + buildRun = nil + } + }) + + Context("Embed BuildSpec in BuildRun", func() { + var outputImage name.Reference + + BeforeEach(func() { + testID = generateTestID("onoff") + + outputImage, err = name.ParseReference(fmt.Sprintf("%s/%s:%s", + os.Getenv(EnvVarImageRepo), + testID, + "latest", + )) + Expect(err).ToNot(HaveOccurred()) + }) + + It("should build an image using Buildpacks and a Git source", func() { + buildRun, err = NewBuildRunPrototype(). + Namespace(testBuild.Namespace). + Name(testID). + WithBuildSpec(NewBuildPrototype(). + ClusterBuildStrategy("buildpacks-v3"). + Namespace(testBuild.Namespace). + Name(testID). + SourceGit("https://github.com/shipwright-io/sample-go.git"). + SourceContextDir("source-build"). + OutputImage(outputImage.String()). + OutputImageCredentials(os.Getenv(EnvVarImageRepoSecret)). + OutputImageInsecure(insecure). + BuildSpec()). + Create() + Expect(err).ToNot(HaveOccurred()) + validateBuildRunToSucceed(testBuild, buildRun) + }) + + It("should build an image using Buildah and a Git source", func() { + buildRun, err = NewBuildRunPrototype(). + Namespace(testBuild.Namespace). + Name(testID). + WithBuildSpec(NewBuildPrototype(). + ClusterBuildStrategy("buildah-shipwright-managed-push"). + Namespace(testBuild.Namespace). + Name(testID). + SourceGit("https://github.com/shipwright-io/sample-go.git"). + SourceContextDir("docker-build"). + Dockerfile("Dockerfile"). + ArrayParamValue("registries-insecure", outputImage.Context().RegistryStr()). + OutputImage(outputImage.String()). + OutputImageCredentials(os.Getenv(EnvVarImageRepoSecret)). + OutputImageInsecure(insecure). + BuildSpec()). + Create() + Expect(err).ToNot(HaveOccurred()) + validateBuildRunToSucceed(testBuild, buildRun) + }) + + It("should build an image using Buildpacks and a ociArtifact source", func() { + buildRun, err = NewBuildRunPrototype(). + Namespace(testBuild.Namespace). + Name(testID). + WithBuildSpec(NewBuildPrototype(). + ClusterBuildStrategy("buildpacks-v3"). + Namespace(testBuild.Namespace). + Name(testID). + SourceBundle("ghcr.io/shipwright-io/sample-go/source-bundle:latest"). + SourceContextDir("source-build"). + OutputImage(outputImage.String()). + OutputImageCredentials(os.Getenv(EnvVarImageRepoSecret)). + OutputImageInsecure(insecure). + BuildSpec()). + Create() + Expect(err).ToNot(HaveOccurred()) + validateBuildRunToSucceed(testBuild, buildRun) + }) + }) +}) diff --git a/test/e2e/v1beta1/e2e_params_test.go b/test/e2e/v1beta1/e2e_params_test.go new file mode 100644 index 000000000..9f018e945 --- /dev/null +++ b/test/e2e/v1beta1/e2e_params_test.go @@ -0,0 +1,138 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" + test "github.com/shipwright-io/build/test/v1beta1_samples" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" +) + +var _ = Describe("For a Kubernetes cluster with Tekton and build installed", func() { + var ( + testID string + err error + + build *buildv1beta1.Build + buildRun *buildv1beta1.BuildRun + buildStrategy *buildv1beta1.BuildStrategy + configMap *corev1.ConfigMap + secret *corev1.Secret + ) + + AfterEach(func() { + if CurrentSpecReport().Failed() { + printTestFailureDebugInfo(testBuild, testBuild.Namespace, testID) + } else if buildRun != nil { + validateServiceAccountDeletion(buildRun, testBuild.Namespace) + } + + if buildRun != nil { + testBuild.DeleteBR(buildRun.Name) + buildRun = nil + } + + if build != nil { + testBuild.DeleteBuild(build.Name) + build = nil + } + + if buildStrategy != nil { + testBuild.DeleteBuildStrategy(buildStrategy.Name) + buildStrategy = nil + } + + if configMap != nil { + testBuild.DeleteConfigMap(configMap.Name) + configMap = nil + } + + if secret != nil { + testBuild.DeleteSecret(secret.Name) + secret = nil + } + }) + + Context("when using a cluster build strategy is used that uses a lot parameters", func() { + BeforeEach(func() { + buildStrategy, err = testBuild.Catalog.LoadBuildStrategyFromBytes([]byte(test.BuildStrategyWithParameterVerification)) + Expect(err).ToNot(HaveOccurred()) + err = testBuild.CreateBuildStrategy(buildStrategy) + Expect(err).ToNot(HaveOccurred()) + }) + + Context("when a secret and a configmap are in place with suitable values", func() { + BeforeEach(func() { + // prepare a ConfigMap + configMap = testBuild.Catalog.ConfigMapWithData("a-configmap", testBuild.Namespace, map[string]string{ + "number1": "1", + "shell": "/bin/bash", + }) + err = testBuild.CreateConfigMap(configMap) + Expect(err).ToNot(HaveOccurred()) + + // prepare a secret + secret = testBuild.Catalog.SecretWithStringData("a-secret", testBuild.Namespace, map[string]string{ + "number2": "2", + "number3": "3", + }) + err = testBuild.CreateSecret(secret) + Expect(err).ToNot(HaveOccurred()) + }) + + Context("when a Build is in place that sets some of the parameters", func() { + BeforeEach(func() { + testID = generateTestID("params") + + build, err = NewBuildPrototype(). + BuildStrategy(buildStrategy.Name). + Name(testID). + Namespace(testBuild.Namespace). + // The source is not actually used by the build, so just take a small one + SourceGit("https://github.com/shipwright-io/sample-go.git"). + // There is not actually an image pushed + OutputImage("dummy"). + // The parameters + StringParamValue("env1", "13"). + StringParamValueFromConfigMap("env2", "a-configmap", "number1", ptr.To[string]("2${CONFIGMAP_VALUE}")). + ArrayParamValueFromConfigMap("commands", "a-configmap", "shell", nil). + ArrayParamValue("commands", "-c"). + Create() + Expect(err).ToNot(HaveOccurred()) + }) + + It("correctly runs a BuildRun that passes the remaining parameters", func() { + buildRun, err = NewBuildRunPrototype(). + ForBuild(build). + Name(testID). + GenerateServiceAccount(). + StringParamValue("image", "registry.access.redhat.com/ubi9/ubi-minimal"). + StringParamValueFromSecret("env3", "a-secret", "number2", nil). + ArrayParamValueFromSecret("args", "a-secret", "number3", ptr.To[string]("${SECRET_VALUE}9")). + ArrayParamValue("args", "47"). + Create() + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + + // we verify the image digest here which is mis-used by the strategy to store a calculated sum + // 13 (env1) + 21 (env2 = 2${a-configmap:number1}) + 2 (env3 = ${a-secret:number2}) + 39 (args[0] = ${a-secret:number3}9) + 47 (args[1]) = 122 + buildRun, err = testBuild.LookupBuildRun(types.NamespacedName{ + Namespace: buildRun.Namespace, + Name: buildRun.Name, + }) + Expect(err).ToNot(HaveOccurred()) + Expect(buildRun.Status.Output).NotTo(BeNil()) + Expect(buildRun.Status.Output.Size).To(BeEquivalentTo(122)) + }) + }) + }) + }) +}) diff --git a/test/e2e/v1beta1/e2e_rbac_test.go b/test/e2e/v1beta1/e2e_rbac_test.go new file mode 100644 index 000000000..6fbd6a89d --- /dev/null +++ b/test/e2e/v1beta1/e2e_rbac_test.go @@ -0,0 +1,75 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var _ = Describe("User RBAC for Shipwright", func() { + + var ctx context.Context + + BeforeEach(func() { + ctx = context.Background() + }) + + It("should install an aggregated edit role for developers", func() { + editRole, err := testBuild.Clientset.RbacV1().ClusterRoles().Get(ctx, "shipwright-build-aggregate-edit", metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + expectedAggregates := []string{ + "rbac.authorization.k8s.io/aggregate-to-edit", + "rbac.authorization.k8s.io/aggregate-to-admin", + } + for _, aggregate := range expectedAggregates { + aggregateValue, exists := editRole.Labels[aggregate] + Expect(exists).To(BeTrue()) + Expect(aggregateValue).To(Equal("true")) + } + // We should have at least two rules - one for ClusterBuildStrategy, another for all else + // More than two rules is acceptable. + Expect(len(editRole.Rules)).To(BeNumerically(">=", 2)) + for _, rule := range editRole.Rules { + Expect(rule.APIGroups).To(ContainElement("shipwright.io")) + for _, resource := range rule.Resources { + if resource == "clusterbuildstrategies" { + Expect(rule.Verbs).To(ContainElements("get", "list", "watch")) + Expect(rule.Verbs).NotTo(ContainElement("create")) + Expect(rule.Verbs).NotTo(ContainElement("update")) + Expect(rule.Verbs).NotTo(ContainElement("patch")) + Expect(rule.Verbs).NotTo(ContainElement("delete")) + } else { + Expect(rule.Verbs).To(ContainElements("get", "list", "watch", "create", "update", "patch", "delete")) + } + } + } + }) + + It("should install an aggregated view role for all users", func() { + viewRole, err := testBuild.Clientset.RbacV1().ClusterRoles().Get(ctx, "shipwright-build-aggregate-view", metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + + aggregateValue, exists := viewRole.Labels["rbac.authorization.k8s.io/aggregate-to-view"] + Expect(exists).To(BeTrue()) + Expect(aggregateValue).To(Equal("true")) + // We should have at least one rule, as this applies "view" permissions to all Shipwright Build objects + // More rules are acceptable for future fine-grained controls. + Expect(len(viewRole.Rules)).To(BeNumerically(">=", 1)) + for _, rule := range viewRole.Rules { + Expect(rule.APIGroups).To(ContainElement("shipwright.io")) + Expect(rule.Verbs).To(ContainElements("get", "list", "watch")) + Expect(rule.Verbs).NotTo(ContainElement("create")) + Expect(rule.Verbs).NotTo(ContainElement("update")) + Expect(rule.Verbs).NotTo(ContainElement("patch")) + Expect(rule.Verbs).NotTo(ContainElement("delete")) + } + }) + +}) diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/v1beta1/e2e_suite_test.go similarity index 94% rename from test/e2e/e2e_suite_test.go rename to test/e2e/v1beta1/e2e_suite_test.go index ff8448cf6..aeed2e27d 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/v1beta1/e2e_suite_test.go @@ -10,8 +10,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - - "github.com/shipwright-io/build/test/utils" + utils "github.com/shipwright-io/build/test/utils/v1beta1" ) var ( diff --git a/test/e2e/v1beta1/e2e_test.go b/test/e2e/v1beta1/e2e_test.go new file mode 100644 index 000000000..0ca8f59e2 --- /dev/null +++ b/test/e2e/v1beta1/e2e_test.go @@ -0,0 +1,685 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "os" + + v1 "github.com/google/go-containerregistry/pkg/v1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" + shpgit "github.com/shipwright-io/build/pkg/git" +) + +var _ = Describe("For a Kubernetes cluster with Tekton and build installed", func() { + var ( + testID string + err error + + build *buildv1beta1.Build + buildRun *buildv1beta1.BuildRun + ) + + AfterEach(func() { + if CurrentSpecReport().Failed() { + printTestFailureDebugInfo(testBuild, testBuild.Namespace, testID) + + } else if buildRun != nil { + validateServiceAccountDeletion(buildRun, testBuild.Namespace) + } + + if buildRun != nil { + testBuild.DeleteBR(buildRun.Name) + buildRun = nil + } + + if build != nil { + testBuild.DeleteBuild(build.Name) + build = nil + } + }) + + Context("when a Buildah build is defined that is using shipwright-managed push", func() { + + BeforeEach(func() { + testID = generateTestID("buildah") + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_buildah_shipwright_managed_push_cr.yaml", + ) + }) + + It("successfully runs a build and surface results to BuildRun", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_buildah_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + appendRegistryInsecureParamValue(build, buildRun) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromGitSource(buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Buildah build is defined that is using strategy-managed push", func() { + + BeforeEach(func() { + testID = generateTestID("buildah") + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_buildah_strategy_managed_push_cr.yaml", + ) + }) + + It("successfully runs a build and surface results to BuildRun", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_buildah_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + appendRegistryInsecureParamValue(build, buildRun) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromGitSource(buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Buildah build with a contextDir and a custom Dockerfile name is defined", func() { + + BeforeEach(func() { + testID = generateTestID("buildah-custom-context-dockerfile") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_buildah_cr_custom_context+dockerfile.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1beta1/buildrun_buildah_cr_custom_context+dockerfile.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + appendRegistryInsecureParamValue(build, buildRun) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a heroku Buildpacks build is defined using a cluster strategy", func() { + + BeforeEach(func() { + testID = generateTestID("buildpacks-v3-heroku") + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_buildpacks-v3-heroku_cr.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_buildpacks-v3-heroku_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a heroku Buildpacks build is defined using a namespaced strategy", func() { + var buildStrategy *buildv1beta1.BuildStrategy + + BeforeEach(func() { + testID = generateTestID("buildpacks-v3-heroku-namespaced") + + buildStrategy, err = buildStrategyTestData(testBuild.Namespace, "samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3-heroku_namespaced_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + err = testBuild.CreateBuildStrategy(buildStrategy) + Expect(err).ToNot(HaveOccurred()) + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_buildpacks-v3-heroku_namespaced_cr.yaml", + ) + }) + + It("successfully runs a build and surface results to BuildRun", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_buildpacks-v3-heroku_namespaced_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromGitSource(buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + + AfterEach(func() { + err = testBuild.DeleteBuildStrategy(buildStrategy.Name) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + Context("when a Buildpacks v3 build is defined using a cluster strategy", func() { + + BeforeEach(func() { + testID = generateTestID("buildpacks-v3") + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_buildpacks-v3_cr.yaml", + ) + }) + + It("successfully runs a build and surface results to BuildRun", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_buildpacks-v3_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromGitSource(buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Buildpacks v3 build is defined using a namespaced strategy", func() { + var buildStrategy *buildv1beta1.BuildStrategy + + BeforeEach(func() { + testID = generateTestID("buildpacks-v3-namespaced") + + buildStrategy, err = buildStrategyTestData(testBuild.Namespace, "samples/v1beta1/buildstrategy/buildpacks-v3/buildstrategy_buildpacks-v3_namespaced_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + err = testBuild.CreateBuildStrategy(buildStrategy) + Expect(err).ToNot(HaveOccurred()) + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_buildpacks-v3_namespaced_cr.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_buildpacks-v3_namespaced_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + + AfterEach(func() { + err = testBuild.DeleteBuildStrategy(buildStrategy.Name) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + Context("when a Buildpacks v3 build is defined for a php runtime", func() { + + BeforeEach(func() { + testID = generateTestID("buildpacks-v3-php") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_buildpacks-v3_php_cr.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1beta1/buildrun_buildpacks-v3_php_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Buildpacks v3 build is defined for a ruby runtime", func() { + + BeforeEach(func() { + testID = generateTestID("buildpacks-v3-ruby") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_buildpacks-v3_ruby_cr.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1beta1/buildrun_buildpacks-v3_ruby_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Buildpacks v3 build is defined for a golang runtime", func() { + + BeforeEach(func() { + testID = generateTestID("buildpacks-v3-golang") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_buildpacks-v3_golang_cr.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1beta1/buildrun_buildpacks-v3_golang_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Buildpacks v3 build is defined for a golang runtime with `BP_GO_TARGETS` env", func() { + BeforeEach(func() { + testID = generateTestID("buildpacks-v3-golang") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_buildpacks-v3_golang_cr_env.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1beta1/buildrun_buildpacks-v3_golang_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a build uses the build-run-deletion annotation", func() { + + BeforeEach(func() { + testID = generateTestID("buildpacks-v3-golang") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_buildpacks-v3_golang_delete_cr.yaml", + ) + }) + + It("successfully deletes the BuildRun after the Build is deleted", func() { + By("running a build and expecting it to succeed") + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1beta1/buildrun_buildpacks-v3_golang_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + + By("deleting the parent Build object") + err = testBuild.DeleteBuild(build.Name) + Expect(err).NotTo(HaveOccurred(), "error deleting the parent Build") + Eventually(func() bool { + _, err = testBuild.GetBR(buildRun.Name) + if err == nil { + return false + } + if !errors.IsNotFound(err) { + return false + } + return true + }).Should(BeTrue()) + }) + }) + + Context("when a Buildpacks v3 build is defined for a java runtime", func() { + + BeforeEach(func() { + testID = generateTestID("buildpacks-v3-java") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_buildpacks-v3_java_cr.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1beta1/buildrun_buildpacks-v3_java_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Kaniko build is defined to use public GitHub", func() { + + BeforeEach(func() { + testID = generateTestID("kaniko") + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_kaniko_cr.yaml", + ) + }) + + It("successfully runs a build and surface results to BuildRun", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_kaniko_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromGitSource(buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Kaniko build with a Dockerfile that requires advanced permissions is defined", func() { + + BeforeEach(func() { + testID = generateTestID("kaniko-advanced-dockerfile") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_kaniko_cr_advanced_dockerfile.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1beta1/buildrun_kaniko_cr_advanced_dockerfile.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Kaniko build with a contextDir and a custom Dockerfile name is defined", func() { + + BeforeEach(func() { + testID = generateTestID("kaniko-custom-context-dockerfile") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_kaniko_cr_custom_context+dockerfile.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "test/data/v1beta1/buildrun_kaniko_cr_custom_context+dockerfile.yaml") + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Kaniko+Trivy build is defined to use an image with no critical CVEs", func() { + + BeforeEach(func() { + testID = generateTestID("kaniko-trivy-good") + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_kaniko-trivy-good_cr.yaml", + ) + }) + + It("successfully runs a build and surface results to BuildRun", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_kaniko-trivy-good_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromGitSource(buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Kaniko+Trivy build is defined to use an image with a critical CVE", func() { + + BeforeEach(func() { + testID = generateTestID("kaniko-trivy-bad") + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_kaniko-trivy-bad_cr.yaml", + ) + }) + + It("fails to run a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_kaniko-trivy-bad_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + validateBuildRunToFail(testBuild, buildRun) + }) + }) + + Context("when a Buildkit build with a contextDir and a path to a Dockerfile is defined", func() { + + BeforeEach(func() { + testID = generateTestID("buildkit-custom-context") + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_buildkit_cr.yaml", + ) + }) + + It("successfully runs a build and surface results to BuildRun", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_buildkit_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromGitSource(buildRun) + testBuild.ValidateImagePlatformsExist(buildRun, []v1.Platform{ + { + Architecture: "amd64", + OS: "linux", + }, + { + Architecture: "arm64", + OS: "linux", + }, + }) + }) + }) + + Context("when a s2i build is defined", func() { + + BeforeEach(func() { + testID = generateTestID("s2i") + + // create the build definition + build = createBuild( + testBuild, + testID, + "samples/v1beta1/build/build_source-to-image_cr.yaml", + ) + }) + + It("successfully runs a build and surface results to BuildRun", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_source-to-image_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + validateBuildRunResultsFromGitSource(buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a private source repository is used", func() { + + BeforeEach(func() { + if os.Getenv(EnvVarEnablePrivateRepos) != "true" { + Skip("Skipping test cases that use a private source repository") + } + }) + + Context("when a Buildah build is defined to use a private GitHub repository", func() { + + BeforeEach(func() { + testID = generateTestID("private-github-buildah") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_buildah_cr_private_github.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_buildah_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Buildah build is defined to use a private GitLab repository", func() { + + BeforeEach(func() { + testID = generateTestID("private-gitlab-buildah") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_buildah_cr_private_gitlab.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_buildah_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Kaniko build is defined to use a private GitHub repository", func() { + + BeforeEach(func() { + testID = generateTestID("private-github-kaniko") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_kaniko_cr_private_github.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_kaniko_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a Kaniko build is defined to use a private GitLab repository", func() { + + BeforeEach(func() { + testID = generateTestID("private-gitlab-kaniko") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_kaniko_cr_private_gitlab.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_kaniko_cr.yaml") + Expect(err).ToNot(HaveOccurred()) + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + + Context("when a s2i build is defined to use a private GitHub repository", func() { + + BeforeEach(func() { + testID = generateTestID("private-github-s2i") + + // create the build definition + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_source-to-image_cr_private_github.yaml", + ) + }) + + It("successfully runs a build", func() { + buildRun, err = buildRunTestData(testBuild.Namespace, testID, "samples/v1beta1/buildrun/buildrun_source-to-image_cr.yaml") + Expect(err).ToNot(HaveOccurred(), "Error retrieving buildrun test data") + + buildRun = validateBuildRunToSucceed(testBuild, buildRun) + testBuild.ValidateImageDigest(buildRun) + }) + }) + }) + + Context("when a s2i build uses a non-existent git repository as source", func() { + It("fails because of prompted authentication which surfaces the to the BuildRun", func() { + testID = generateTestID("s2i-failing") + + build = createBuild( + testBuild, + testID, + "test/data/v1beta1/build_non_existing_repo.yaml", + ) + + buildRun, err = buildRunTestData(build.Namespace, testID, "test/data/v1beta1/buildrun_non_existing_repo.yaml") + Expect(err).ToNot(HaveOccurred()) + + validateBuildRunToFail(testBuild, buildRun) + buildRun, err = testBuild.LookupBuildRun(types.NamespacedName{Name: buildRun.Name, Namespace: testBuild.Namespace}) + + Expect(buildRun.Status.FailureDetails.Message).To(Equal(shpgit.AuthPrompted.ToMessage())) + Expect(buildRun.Status.FailureDetails.Reason).To(Equal(shpgit.AuthPrompted.String())) + }) + }) + +}) diff --git a/test/e2e/v1beta1/validators_test.go b/test/e2e/v1beta1/validators_test.go new file mode 100644 index 000000000..34cc8e99d --- /dev/null +++ b/test/e2e/v1beta1/validators_test.go @@ -0,0 +1,371 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/kubectl/pkg/scheme" + + "github.com/shipwright-io/build/pkg/apis" + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" + utils "github.com/shipwright-io/build/test/utils/v1beta1" +) + +const ( + EnvVarServiceAccountName = "TEST_E2E_SERVICEACCOUNT_NAME" + EnvVarVerifyTektonObjects = "TEST_E2E_VERIFY_TEKTONOBJECTS" + EnvVarTimeoutMultiplier = "TEST_E2E_TIMEOUT_MULTIPLIER" + EnvVarImageRepo = "TEST_IMAGE_REPO" + EnvVarImageRepoInsecure = "TEST_IMAGE_REPO_INSECURE" + EnvVarEnablePrivateRepos = "TEST_PRIVATE_REPO" + EnvVarImageRepoSecret = "TEST_IMAGE_REPO_SECRET" + EnvVarSourceRepoSecretJSON = "TEST_IMAGE_REPO_DOCKERCONFIGJSON" + EnvVarSourceURLGithub = "TEST_PRIVATE_GITHUB" + EnvVarSourceURLGitlab = "TEST_PRIVATE_GITLAB" + EnvVarSourceURLSecret = "TEST_SOURCE_SECRET" +) + +// createPipelineServiceAccount reads the TEST_E2E_SERVICEACCOUNT_NAME environment variable. If the value is "generated", then nothing is done. +// Otherwise it will create the service account. No error occurs if the service account already exists. +func createPipelineServiceAccount(testBuild *utils.TestBuild) { + serviceAccountName, ok := os.LookupEnv(EnvVarServiceAccountName) + Expect(ok).To(BeTrue(), "environment variable "+EnvVarServiceAccountName+" is not set") + Expect(serviceAccountName).ToNot(BeEmpty()) + + if serviceAccountName == "generated" { + Logf("Skipping creation of service account, generated one will be used per build run.") + return + } + + if _, err := testBuild.LookupServiceAccount(types.NamespacedName{Namespace: testBuild.Namespace, Name: serviceAccountName}); err == nil { + Logf("Skipping creation of service account, reusing existing one.") + return + } + + Logf("Creating '%s' service-account", serviceAccountName) + _, err := testBuild.Clientset.CoreV1(). + ServiceAccounts(testBuild.Namespace). + Create(testBuild.Context, + &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testBuild.Namespace, + Name: serviceAccountName, + }}, + metav1.CreateOptions{}) + + // Due to concurrency, it could be that some other routine already finished creating the service-account + if err != nil && apierrors.IsAlreadyExists(err) { + Logf("Creation failed, because service-account %q is already in the system.", serviceAccountName) + return + } + + Expect(err).ToNot(HaveOccurred(), "Error creating service account") +} + +// createContainerRegistrySecret use environment variables to check for container registry +// credentials secret, when not found a new secret is created. +func createContainerRegistrySecret(testBuild *utils.TestBuild) { + secretName := os.Getenv(EnvVarImageRepoSecret) + secretPayload := os.Getenv(EnvVarSourceRepoSecretJSON) + if secretName == "" || secretPayload == "" { + Logf("Container registry secret won't be created.") + return + } + + _, err := testBuild.LookupSecret(types.NamespacedName{Namespace: testBuild.Namespace, Name: secretName}) + if err == nil { + Logf("Container registry secret is found at '%s/%s'", testBuild.Namespace, secretName) + return + } + + payload := []byte(secretPayload) + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testBuild.Namespace, + Name: secretName, + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + ".dockerconfigjson": payload, + }, + } + + Logf("Creating container-registry secret '%s/%s' (%d bytes)", testBuild.Namespace, secretName, len(payload)) + err = testBuild.CreateSecret(secret) + Expect(err).ToNot(HaveOccurred(), "on creating container registry secret") +} + +// validateBuildRunToSucceed creates the build run and watches its flow until it succeeds. +func validateBuildRunToSucceed(testBuild *utils.TestBuild, testBuildRun *buildv1beta1.BuildRun) *buildv1beta1.BuildRun { + trueCondition := corev1.ConditionTrue + falseCondition := corev1.ConditionFalse + + // Ensure the BuildRun has been created + if _, err := testBuild.GetBR(testBuildRun.Name); err != nil { + Expect(testBuild.CreateBR(testBuildRun)). + ToNot(HaveOccurred(), "Failed to create BuildRun") + } + + // Ensure a BuildRun eventually moves to a succeeded TRUE status + nextStatusLog := time.Now().Add(60 * time.Second) + Eventually(func() corev1.ConditionStatus { + testBuildRun, err := testBuild.LookupBuildRun(types.NamespacedName{Name: testBuildRun.Name, Namespace: testBuild.Namespace}) + Expect(err).ToNot(HaveOccurred(), "Error retrieving a buildRun") + + if testBuildRun.Status.GetCondition(buildv1beta1.Succeeded) == nil { + return corev1.ConditionUnknown + } + + Expect(testBuildRun.Status.GetCondition(buildv1beta1.Succeeded).Status).ToNot(Equal(falseCondition), "BuildRun status doesn't move to Succeeded") + + now := time.Now() + if now.After(nextStatusLog) { + Logf("Still waiting for build run '%s' to succeed.", testBuildRun.Name) + nextStatusLog = time.Now().Add(60 * time.Second) + } + + return testBuildRun.Status.GetCondition(buildv1beta1.Succeeded).Status + + }, time.Duration(1100*getTimeoutMultiplier())*time.Second, 5*time.Second).Should(Equal(trueCondition), "BuildRun did not succeed") + + // Verify that the BuildSpec is still available in the status + testBuildRun, err := testBuild.GetBR(testBuildRun.Name) + Expect(err).ToNot(HaveOccurred()) + Expect(testBuildRun.Status.BuildSpec).ToNot(BeNil(), "BuildSpec is not available in the status") + + Logf("Test build '%s' is completed after %v !", testBuildRun.GetName(), testBuildRun.Status.CompletionTime.Time.Sub(testBuildRun.Status.StartTime.Time)) + + return testBuildRun +} + +func validateBuildRunResultsFromGitSource(testBuildRun *buildv1beta1.BuildRun) { + testBuildRun, err := testBuild.GetBR(testBuildRun.Name) + Expect(err).ToNot(HaveOccurred()) + + Expect(len(testBuildRun.Status.Sources)).To(Equal(1)) + + // Only run the TaskRun checks if Tekton objects can be accessed + if os.Getenv(EnvVarVerifyTektonObjects) == "true" { + tr, err := testBuild.GetTaskRunFromBuildRun(testBuildRun.Name) + Expect(err).ToNot(HaveOccurred()) + + for _, result := range tr.Status.TaskRunResults { + switch result.Name { + case "shp-source-default-commit-sha": + Expect(result.Value.StringVal).To(Equal(testBuildRun.Status.Sources[0].Git.CommitSha)) + case "shp-source-default-commit-author": + Expect(result.Value.StringVal).To(Equal(testBuildRun.Status.Sources[0].Git.CommitAuthor)) + case "shp-source-default-branch-name": + Expect(result.Value.StringVal).To(Equal(testBuildRun.Status.Sources[0].Git.BranchName)) + case "shp-image-digest": + Expect(result.Value.StringVal).To(Equal(testBuildRun.Status.Output.Digest)) + case "shp-image-size": + size, err := strconv.ParseInt(result.Value.StringVal, 10, 64) + Expect(err).To(BeNil()) + Expect(size).To(Equal(testBuildRun.Status.Output.Size)) + } + } + } +} + +func validateBuildRunResultsFromBundleSource(testBuildRun *buildv1beta1.BuildRun) { + testBuildRun, err := testBuild.GetBR(testBuildRun.Name) + Expect(err).ToNot(HaveOccurred()) + + Expect(len(testBuildRun.Status.Sources)).To(Equal(1)) + + // Only run the TaskRun checks if Tekton objects can be accessed + if os.Getenv(EnvVarVerifyTektonObjects) == "true" { + tr, err := testBuild.GetTaskRunFromBuildRun(testBuildRun.Name) + Expect(err).ToNot(HaveOccurred()) + + for _, result := range tr.Status.TaskRunResults { + switch result.Name { + case "shp-source-default-image-digest": + Expect(result.Value.StringVal).To(Equal(testBuildRun.Status.Sources[0].OciArtifact.Digest)) + case "shp-image-digest": + Expect(result.Value.StringVal).To(Equal(testBuildRun.Status.Output.Digest)) + case "shp-image-size": + size, err := strconv.ParseInt(result.Value.StringVal, 10, 64) + Expect(err).To(BeNil()) + Expect(size).To(Equal(testBuildRun.Status.Output.Size)) + } + } + } +} + +// validateBuildRunToFail creates the build run and watches its flow until it fails. +func validateBuildRunToFail(testBuild *utils.TestBuild, testBuildRun *buildv1beta1.BuildRun) { + trueCondition := corev1.ConditionTrue + falseCondition := corev1.ConditionFalse + + // Ensure the BuildRun has been created + err := testBuild.CreateBR(testBuildRun) + Expect(err).ToNot(HaveOccurred(), "Failed to create BuildRun") + + // Ensure a BuildRun eventually moves to a succeeded FALSE status + nextStatusLog := time.Now().Add(60 * time.Second) + Eventually(func() corev1.ConditionStatus { + testBuildRun, err = testBuild.LookupBuildRun(types.NamespacedName{Name: testBuildRun.Name, Namespace: testBuild.Namespace}) + Expect(err).ToNot(HaveOccurred(), "Error retrieving a buildRun") + + if testBuildRun.Status.GetCondition(buildv1beta1.Succeeded) == nil { + return corev1.ConditionUnknown + } + + Expect(testBuildRun.Status.GetCondition(buildv1beta1.Succeeded).Status).NotTo(Equal(trueCondition), "BuildRun status moves to Succeeded") + + now := time.Now() + if now.After(nextStatusLog) { + Logf("Still waiting for build run '%s' to fail.", testBuildRun.Name) + nextStatusLog = time.Now().Add(60 * time.Second) + } + + return testBuildRun.Status.GetCondition(buildv1beta1.Succeeded).Status + + }, time.Duration(1100*getTimeoutMultiplier())*time.Second, 5*time.Second).Should(Equal(falseCondition), "BuildRun did not succeed") + + // Verify that the BuildSpec is still available in the status + Expect(testBuildRun.Status.BuildSpec).ToNot(BeNil(), "BuildSpec is not available in the status") + + Logf("Test build '%s' is completed after %v !", testBuildRun.GetName(), testBuildRun.Status.CompletionTime.Time.Sub(testBuildRun.Status.StartTime.Time)) +} + +// validateServiceAccountDeletion validates that a service account is correctly deleted after the end of +// a build run and depending on the state of the build run +func validateServiceAccountDeletion(buildRun *buildv1beta1.BuildRun, namespace string) { + buildRunCondition := buildRun.Status.GetCondition(buildv1beta1.Succeeded) + if buildRunCondition != nil { + if buildRunCondition.Status == "" || buildRunCondition.Status == corev1.ConditionUnknown { + Logf("Skipping validation of service account deletion because build run did not end.") + return + } + } + + if buildRun.Spec.ServiceAccount == nil { + Logf("Skipping validation of service account deletion because service account is not generated") + return + } + + saNamespacedName := types.NamespacedName{ + Name: buildRun.Name, + Namespace: namespace, + } + + Logf("Verifying that service account '%s' has been deleted.", saNamespacedName.Name) + _, err := testBuild.LookupServiceAccount(saNamespacedName) + Expect(err).To(HaveOccurred(), "Expected error to retrieve the generated service account after build run completion.") + Expect(apierrors.IsNotFound(err)).To(BeTrue(), "Expected service account to be deleted.") +} + +func readAndDecode(filePath string) (runtime.Object, error) { + decode := scheme.Codecs.UniversalDeserializer().Decode + if err := apis.AddToScheme(scheme.Scheme); err != nil { + return nil, err + } + + payload, err := os.ReadFile(filepath.Join("..", "..", "..", filePath)) + if err != nil { + return nil, err + } + + obj, _, err := decode(payload, nil, nil) + return obj, err +} + +// buildStrategyTestData gets the us the BuildStrategy test data set up +func buildStrategyTestData(ns string, buildStrategyCRPath string) (*buildv1beta1.BuildStrategy, error) { + obj, err := readAndDecode(buildStrategyCRPath) + if err != nil { + return nil, err + } + + buildStrategy := obj.(*buildv1beta1.BuildStrategy) + buildStrategy.SetNamespace(ns) + + return buildStrategy, err +} + +func buildTestData(namespace string, identifier string, filePath string) (*buildv1beta1.Build, error) { + obj, err := readAndDecode(filePath) + if err != nil { + return nil, err + } + + build, ok := obj.(*buildv1beta1.Build) + if !ok { + return nil, fmt.Errorf("failed to use the content of %s as a Build runtime object", filePath) + } + + build.SetNamespace(namespace) + build.SetName(identifier) + return build, nil +} + +// buildTestData gets the us the Build test data set up +func buildRunTestData(ns string, identifier string, filePath string) (*buildv1beta1.BuildRun, error) { + obj, err := readAndDecode(filePath) + if err != nil { + return nil, err + } + + buildRun, ok := obj.(*buildv1beta1.BuildRun) + if !ok { + return nil, fmt.Errorf("failed to use the content of %s as a BuildRun runtime object", filePath) + } + + buildRun.SetNamespace(ns) + buildRun.SetName(identifier) + buildRun.Spec.Build.Name = identifier + + serviceAccountName := os.Getenv(EnvVarServiceAccountName) + if serviceAccountName == "generated" { + generate := ".generate" + buildRun.Spec.ServiceAccount = &generate + } else { + buildRun.Spec.ServiceAccount = &serviceAccountName + } + + return buildRun, nil +} + +func appendRegistryInsecureParamValue(build *buildv1beta1.Build, buildRun *buildv1beta1.BuildRun) { + if strings.Contains(build.Spec.Output.Image, "cluster.local") { + parts := strings.Split(build.Spec.Output.Image, "/") + host := parts[0] + buildRun.Spec.ParamValues = append(buildRun.Spec.ParamValues, buildv1beta1.ParamValue{ + Name: "registries-insecure", + Values: []buildv1beta1.SingleValue{ + { + Value: &host, + }, + }, + }) + } +} + +func getTimeoutMultiplier() int64 { + value := os.Getenv(EnvVarTimeoutMultiplier) + if value == "" { + return 1 + } + + intValue, err := strconv.ParseInt(value, 10, 64) + Expect(err).ToNot(HaveOccurred(), "Failed to parse EnvVarTimeoutMultiplier to integer") + return intValue +} diff --git a/test/integration/build_to_buildruns_test.go b/test/integration/build_to_buildruns_test.go index 0748562ef..ccae8c58a 100644 --- a/test/integration/build_to_buildruns_test.go +++ b/test/integration/build_to_buildruns_test.go @@ -17,7 +17,7 @@ import ( "k8s.io/apimachinery/pkg/types" "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test" + test "github.com/shipwright-io/build/test/v1alpha1_samples" ) var _ = Describe("Integration tests Build and BuildRuns", func() { diff --git a/test/integration/build_to_git_test.go b/test/integration/build_to_git_test.go index 745f4e207..0dc6f3777 100644 --- a/test/integration/build_to_git_test.go +++ b/test/integration/build_to_git_test.go @@ -8,9 +8,9 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test" + test "github.com/shipwright-io/build/test/v1alpha1_samples" corev1 "k8s.io/api/core/v1" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) var _ = Describe("Integration tests Build and referenced Source url", func() { @@ -47,7 +47,7 @@ var _ = Describe("Integration tests Build and referenced Source url", func() { Expect(err).To(BeNil()) buildObject.ObjectMeta.Annotations["build.shipwright.io/verify.repository"] = "true" - buildObject.Spec.Source.URL = pointer.String("http://github.com/shipwright-io/sample-go") + buildObject.Spec.Source.URL = ptr.To[string]("http://github.com/shipwright-io/sample-go") Expect(tb.CreateBuild(buildObject)).To(BeNil()) @@ -72,7 +72,7 @@ var _ = Describe("Integration tests Build and referenced Source url", func() { ) Expect(err).To(BeNil()) - buildObject.Spec.Source.URL = pointer.String("http://github.com/shipwright-io/sample-go-fake") + buildObject.Spec.Source.URL = ptr.To[string]("http://github.com/shipwright-io/sample-go-fake") Expect(tb.CreateBuild(buildObject)).To(BeNil()) // wait until the Build finish the validation @@ -98,7 +98,7 @@ var _ = Describe("Integration tests Build and referenced Source url", func() { Expect(err).To(BeNil()) buildObject.ObjectMeta.Annotations["build.shipwright.io/verify.repository"] = "true" - buildObject.Spec.Source.URL = pointer.String("https://github.com/shipwright-io/sample-go") + buildObject.Spec.Source.URL = ptr.To[string]("https://github.com/shipwright-io/sample-go") Expect(tb.CreateBuild(buildObject)).To(BeNil()) @@ -123,7 +123,7 @@ var _ = Describe("Integration tests Build and referenced Source url", func() { ) Expect(err).To(BeNil()) - buildObject.Spec.Source.URL = pointer.String("https://github.com/shipwright-io/sample-go-fake") + buildObject.Spec.Source.URL = ptr.To[string]("https://github.com/shipwright-io/sample-go-fake") Expect(tb.CreateBuild(buildObject)).To(BeNil()) // wait until the Build finish the validation @@ -149,7 +149,7 @@ var _ = Describe("Integration tests Build and referenced Source url", func() { Expect(err).To(BeNil()) buildObject.ObjectMeta.Annotations["build.shipwright.io/verify.repository"] = "true" - buildObject.Spec.Source.URL = pointer.String("foobar") + buildObject.Spec.Source.URL = ptr.To[string]("foobar") Expect(tb.CreateBuild(buildObject)).To(BeNil()) @@ -176,7 +176,7 @@ var _ = Describe("Integration tests Build and referenced Source url", func() { Expect(err).To(BeNil()) buildObject.ObjectMeta.Annotations["build.shipwright.io/verify.repository"] = "false" - buildObject.Spec.Source.URL = pointer.String("foobar") + buildObject.Spec.Source.URL = ptr.To[string]("foobar") Expect(tb.CreateBuild(buildObject)).To(BeNil()) // wait until the Build finish the validation @@ -202,7 +202,7 @@ var _ = Describe("Integration tests Build and referenced Source url", func() { Expect(err).To(BeNil()) buildObject.ObjectMeta.Annotations["build.shipwright.io/verify.repository"] = "true" - buildObject.Spec.Source.URL = pointer.String("https://github.yourco.com/org/build-fake") + buildObject.Spec.Source.URL = ptr.To[string]("https://github.yourco.com/org/build-fake") Expect(tb.CreateBuild(buildObject)).To(BeNil()) @@ -228,7 +228,7 @@ var _ = Describe("Integration tests Build and referenced Source url", func() { Expect(err).To(BeNil()) buildObject.ObjectMeta.Annotations["build.shipwright.io/verify.repository"] = "true" - buildObject.Spec.Source.URL = pointer.String("https://github.yourco.com/org/build-fake") + buildObject.Spec.Source.URL = ptr.To[string]("https://github.yourco.com/org/build-fake") buildObject.Spec.Source.Credentials = &corev1.LocalObjectReference{Name: "foobar"} sampleSecret := tb.Catalog.SecretWithAnnotation(buildObject.Spec.Source.Credentials.Name, buildObject.Namespace) @@ -260,7 +260,7 @@ var _ = Describe("Integration tests Build and referenced Source url", func() { Expect(err).To(BeNil()) buildObject.ObjectMeta.Annotations["build.shipwright.io/verify.repository"] = "true" - buildObject.Spec.Source.URL = pointer.String("git@github.com:shipwright-io/build-fake.git") + buildObject.Spec.Source.URL = ptr.To[string]("git@github.com:shipwright-io/build-fake.git") Expect(tb.CreateBuild(buildObject)).To(BeNil()) @@ -288,7 +288,7 @@ var _ = Describe("Integration tests Build and referenced Source url", func() { Expect(err).To(BeNil()) buildObject.ObjectMeta.Annotations["build.shipwright.io/verify.repository"] = "true" - buildObject.Spec.Source.URL = pointer.String("ssh://github.com/shipwright-io/build-fake.git") + buildObject.Spec.Source.URL = ptr.To[string]("ssh://github.com/shipwright-io/build-fake.git") Expect(tb.CreateBuild(buildObject)).To(BeNil()) diff --git a/test/integration/build_to_secrets_test.go b/test/integration/build_to_secrets_test.go index 2fa6cb542..dcb476eca 100644 --- a/test/integration/build_to_secrets_test.go +++ b/test/integration/build_to_secrets_test.go @@ -10,7 +10,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test" + test "github.com/shipwright-io/build/test/v1alpha1_samples" corev1 "k8s.io/api/core/v1" ) diff --git a/test/integration/build_to_taskruns_test.go b/test/integration/build_to_taskruns_test.go index a6469fd5d..6766cfcd3 100644 --- a/test/integration/build_to_taskruns_test.go +++ b/test/integration/build_to_taskruns_test.go @@ -10,8 +10,8 @@ import ( corev1 "k8s.io/api/core/v1" "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test" - "github.com/shipwright-io/build/test/utils" + utils "github.com/shipwright-io/build/test/utils/v1alpha1" + test "github.com/shipwright-io/build/test/v1alpha1_samples" ) var _ = Describe("Integration tests Build and TaskRun", func() { diff --git a/test/integration/buildrun_cleanup_test.go b/test/integration/buildrun_cleanup_test.go index 51c587ad6..f5ba42d99 100644 --- a/test/integration/buildrun_cleanup_test.go +++ b/test/integration/buildrun_cleanup_test.go @@ -9,7 +9,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test" + test "github.com/shipwright-io/build/test/v1alpha1_samples" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" ) diff --git a/test/integration/buildruns_to_sa_test.go b/test/integration/buildruns_to_sa_test.go index c7d09d9bf..85e998bb0 100644 --- a/test/integration/buildruns_to_sa_test.go +++ b/test/integration/buildruns_to_sa_test.go @@ -15,7 +15,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test" + test "github.com/shipwright-io/build/test/v1alpha1_samples" ) var _ = Describe("Integration tests BuildRuns and Service-accounts", func() { diff --git a/test/integration/buildruns_to_taskruns_test.go b/test/integration/buildruns_to_taskruns_test.go index 9e3a32400..5c80107f8 100644 --- a/test/integration/buildruns_to_taskruns_test.go +++ b/test/integration/buildruns_to_taskruns_test.go @@ -16,7 +16,7 @@ import ( "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" "github.com/shipwright-io/build/pkg/reconciler/buildrun/resources" - "github.com/shipwright-io/build/test" + test "github.com/shipwright-io/build/test/v1alpha1_samples" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/test/integration/buildstrategy_to_taskruns_test.go b/test/integration/buildstrategy_to_taskruns_test.go index 1da1daf14..162bf1d4a 100644 --- a/test/integration/buildstrategy_to_taskruns_test.go +++ b/test/integration/buildstrategy_to_taskruns_test.go @@ -11,8 +11,8 @@ import ( . "github.com/onsi/gomega" "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test" - "github.com/shipwright-io/build/test/utils" + utils "github.com/shipwright-io/build/test/utils/v1alpha1" + test "github.com/shipwright-io/build/test/v1alpha1_samples" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" corev1 "k8s.io/api/core/v1" ) diff --git a/test/integration/clusterbuildstrategy_to_taskruns_test.go b/test/integration/clusterbuildstrategy_to_taskruns_test.go index 1f1d53bda..1fce84628 100644 --- a/test/integration/clusterbuildstrategy_to_taskruns_test.go +++ b/test/integration/clusterbuildstrategy_to_taskruns_test.go @@ -9,7 +9,7 @@ import ( . "github.com/onsi/gomega" "github.com/shipwright-io/build/pkg/apis/build/v1alpha1" - "github.com/shipwright-io/build/test" + test "github.com/shipwright-io/build/test/v1alpha1_samples" ) var _ = Describe("Integration tests ClusterBuildStrategies and TaskRuns", func() { @@ -55,13 +55,13 @@ var _ = Describe("Integration tests ClusterBuildStrategies and TaskRuns", func() } }) - Context("when a buildrun is created", func() { + Context("when a buildrun is created", func() { BeforeEach(func() { buildSample = []byte(test.BuildCBSMinimal) buildRunSample = []byte(test.MinimalBuildRun) }) - It("should create a taskrun with the correct annotations", func() { + It("should create a taskrun with the correct annotations", func() { Expect(tb.CreateBuild(buildObject)).To(BeNil()) buildObject, err = tb.GetBuildTillValidation(buildObject.Name) diff --git a/test/integration/integration_suite_test.go b/test/integration/integration_suite_test.go index 5a21bc314..75ae2bc70 100644 --- a/test/integration/integration_suite_test.go +++ b/test/integration/integration_suite_test.go @@ -12,7 +12,8 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/shipwright-io/build/test/utils" + // utils "github.com/shipwright-io/build/test/utils" + utils "github.com/shipwright-io/build/test/utils/v1alpha1" ) const ( diff --git a/test/utils/buildruns.go b/test/utils/v1alpha1/buildruns.go similarity index 100% rename from test/utils/buildruns.go rename to test/utils/v1alpha1/buildruns.go diff --git a/test/utils/builds.go b/test/utils/v1alpha1/builds.go similarity index 100% rename from test/utils/builds.go rename to test/utils/v1alpha1/builds.go diff --git a/test/utils/buildstrategies.go b/test/utils/v1alpha1/buildstrategies.go similarity index 100% rename from test/utils/buildstrategies.go rename to test/utils/v1alpha1/buildstrategies.go diff --git a/test/utils/clusterbuildstrategies.go b/test/utils/v1alpha1/clusterbuildstrategies.go similarity index 100% rename from test/utils/clusterbuildstrategies.go rename to test/utils/v1alpha1/clusterbuildstrategies.go diff --git a/test/utils/configmaps.go b/test/utils/v1alpha1/configmaps.go similarity index 100% rename from test/utils/configmaps.go rename to test/utils/v1alpha1/configmaps.go diff --git a/test/utils/controllers.go b/test/utils/v1alpha1/controllers.go similarity index 100% rename from test/utils/controllers.go rename to test/utils/v1alpha1/controllers.go diff --git a/test/utils/v1alpha1/environment.go b/test/utils/v1alpha1/environment.go new file mode 100644 index 000000000..b614f782f --- /dev/null +++ b/test/utils/v1alpha1/environment.go @@ -0,0 +1,128 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "bytes" + "context" + "os" + "path/filepath" + "strconv" + "sync/atomic" + "time" + + "github.com/onsi/ginkgo/v2" + tektonClient "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + + "k8s.io/client-go/kubernetes" + _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/client" + + buildClient "github.com/shipwright-io/build/pkg/client/clientset/versioned" + "github.com/shipwright-io/build/pkg/ctxlog" + test "github.com/shipwright-io/build/test/v1alpha1_samples" + // from https://github.com/kubernetes/client-go/issues/345 +) + +var ( + namespaceCounter int32 +) + +// TestBuild wraps all required clients to run integration +// tests and also the namespace and operator channel used +// per each test case +type TestBuild struct { + // TODO: Adding specific field for polling here, interval and timeout + // but I think we need a small refactoring to make them global for all + // tests under /test dir + Interval time.Duration + TimeOut time.Duration + KubeConfig *rest.Config + Clientset *kubernetes.Clientset + Namespace string + StopBuildControllers context.CancelFunc + BuildClientSet *buildClient.Clientset + PipelineClientSet *tektonClient.Clientset + ControllerRuntimeClient client.Client + Catalog test.Catalog + Context context.Context + BuildControllerLogBuffer *bytes.Buffer +} + +// NewTestBuild returns an initialized instance of TestBuild +func NewTestBuild() (*TestBuild, error) { + namespaceID := ginkgo.GinkgoParallelProcess()*200 + int(atomic.AddInt32(&namespaceCounter, 1)) + testNamespace := "test-build-" + strconv.Itoa(namespaceID) + + logBuffer := &bytes.Buffer{} + l := ctxlog.NewLoggerTo(logBuffer, testNamespace) + + ctx := ctxlog.NewParentContext(l) + + kubeConfig, restConfig, err := KubeConfig() + if err != nil { + return nil, err + } + + // clientSet is required to communicate with our CRDs objects + // see https://www.openshift.com/blog/kubernetes-deep-dive-code-generation-customresources + buildClientSet, err := buildClient.NewForConfig(restConfig) + if err != nil { + return nil, err + } + + pipelineClientSet, err := tektonClient.NewForConfig(restConfig) + if err != nil { + return nil, err + } + + controllerRuntimeClient, err := client.New(restConfig, client.Options{}) + if err != nil { + return nil, err + } + + ctx, cancelFn := context.WithCancel(ctx) + + return &TestBuild{ + // TODO: interval and timeout can be configured via ENV vars + Interval: time.Second * 3, + TimeOut: time.Second * 180, + KubeConfig: restConfig, + Clientset: kubeConfig, + Namespace: testNamespace, + BuildClientSet: buildClientSet, + PipelineClientSet: pipelineClientSet, + ControllerRuntimeClient: controllerRuntimeClient, + Context: ctx, + BuildControllerLogBuffer: logBuffer, + StopBuildControllers: cancelFn, + }, nil +} + +// KubeConfig returns all required clients to speak with +// the k8s API +func KubeConfig() (*kubernetes.Clientset, *rest.Config, error) { + location := os.Getenv("KUBECONFIG") + if location == "" { + location = filepath.Join(os.Getenv("HOME"), ".kube", "config") + } + + config, err := clientcmd.BuildConfigFromFlags("", location) + if err != nil { + config, err = rest.InClusterConfig() + if err != nil { + return nil, nil, err + } + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, nil, err + } + + return clientset, config, nil +} diff --git a/test/utils/gomega.go b/test/utils/v1alpha1/gomega.go similarity index 100% rename from test/utils/gomega.go rename to test/utils/v1alpha1/gomega.go diff --git a/test/utils/image.go b/test/utils/v1alpha1/image.go similarity index 100% rename from test/utils/image.go rename to test/utils/v1alpha1/image.go diff --git a/test/utils/lookup.go b/test/utils/v1alpha1/lookup.go similarity index 100% rename from test/utils/lookup.go rename to test/utils/v1alpha1/lookup.go diff --git a/test/utils/namespaces.go b/test/utils/v1alpha1/namespaces.go similarity index 100% rename from test/utils/namespaces.go rename to test/utils/v1alpha1/namespaces.go diff --git a/test/utils/secrets.go b/test/utils/v1alpha1/secrets.go similarity index 100% rename from test/utils/secrets.go rename to test/utils/v1alpha1/secrets.go diff --git a/test/utils/service_accounts.go b/test/utils/v1alpha1/service_accounts.go similarity index 100% rename from test/utils/service_accounts.go rename to test/utils/v1alpha1/service_accounts.go diff --git a/test/utils/taskruns.go b/test/utils/v1alpha1/taskruns.go similarity index 100% rename from test/utils/taskruns.go rename to test/utils/v1alpha1/taskruns.go diff --git a/test/utils/v1alpha1/webhook.go b/test/utils/v1alpha1/webhook.go new file mode 100644 index 000000000..731a7133e --- /dev/null +++ b/test/utils/v1alpha1/webhook.go @@ -0,0 +1,110 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + "crypto/tls" + "net/http" + "time" + + "github.com/shipwright-io/build/pkg/webhook/conversion" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +func StartBuildWebhook() *http.Server { + mux := http.NewServeMux() + mux.HandleFunc("/convert", conversion.CRDConvertHandler(context.Background())) + mux.HandleFunc("/health", health) + + webhookServer := &http.Server{ + Addr: ":30443", + Handler: mux, + ReadHeaderTimeout: 32 * time.Second, + IdleTimeout: time.Second, + TLSConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{tls.CurveP256, tls.CurveP384, tls.X25519}, + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + }, + }, + } + + // start server + go func() { + defer ginkgo.GinkgoRecover() + + if err := webhookServer.ListenAndServeTLS("/tmp/server-cert.pem", "/tmp/server-key.pem"); err != nil { + if err != http.ErrServerClosed { + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + } + } + }() + + client := &http.Client{ + Transport: &http.Transport{ + IdleConnTimeout: 5 * time.Second, + ResponseHeaderTimeout: 5 * time.Second, + // #nosec:G402 test code + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + TLSHandshakeTimeout: 5 * time.Second, + }, + } + + gomega.Eventually(func() int { + r, err := client.Get("https://localhost:30443/health") + if err != nil { + return 0 + } + if r != nil { + return r.StatusCode + } + return 0 + }).WithTimeout(10 * time.Second).Should(gomega.Equal(http.StatusNoContent)) + + return webhookServer +} + +func StopBuildWebhook(webhookServer *http.Server) { + err := webhookServer.Close() + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + + client := &http.Client{ + Transport: &http.Transport{ + IdleConnTimeout: 5 * time.Second, + ResponseHeaderTimeout: 5 * time.Second, + // #nosec:G402 test code + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + TLSHandshakeTimeout: 5 * time.Second, + }, + } + + gomega.Eventually(func() int { + r, err := client.Get("https://localhost:30443/health") + if err != nil { + return 0 + } + if r != nil { + return r.StatusCode + } + return 0 + }).WithTimeout(10 * time.Second).Should(gomega.Equal(0)) +} + +func health(resp http.ResponseWriter, _ *http.Request) { + resp.WriteHeader(http.StatusNoContent) +} diff --git a/test/utils/v1beta1/buildruns.go b/test/utils/v1beta1/buildruns.go new file mode 100644 index 000000000..2bb4c5d05 --- /dev/null +++ b/test/utils/v1beta1/buildruns.go @@ -0,0 +1,283 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "time" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + + "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +// This class is intended to host all CRUD calls for testing BuildRun CRDs resources + +// CreateBR generates a BuildRun on the current test namespace +func (t *TestBuild) CreateBR(buildRun *v1beta1.BuildRun) error { + brInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + _, err := brInterface.Create(context.TODO(), buildRun, metav1.CreateOptions{}) + if err != nil { + return err + } + return nil +} + +// UpdateBR updates a BuildRun on the current test namespace +func (t *TestBuild) UpdateBR(buildRun *v1beta1.BuildRun) error { + brInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + _, err := brInterface.Update(context.TODO(), buildRun, metav1.UpdateOptions{}) + if err != nil { + return err + } + return nil +} + +// GetBR retrieves a BuildRun from a desired namespace +// Deprecated: Use LookupBuildRun instead. +func (t *TestBuild) GetBR(name string) (*v1beta1.BuildRun, error) { + brInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + br, err := brInterface.Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return br, nil +} + +// DeleteBR deletes a BuildRun from a desired namespace +func (t *TestBuild) DeleteBR(name string) error { + brInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + if err := brInterface.Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil { + return err + } + + return nil +} + +// GetBRReason ... +func (t *TestBuild) GetBRReason(name string) (string, error) { + br, err := t.GetBR(name) + if err != nil { + return "", err + } + cond := br.Status.GetCondition(v1beta1.Succeeded) + if cond == nil { + return "", errors.New("BuildRun had no Succeeded condition") + } + return cond.Reason, nil +} + +// GetBRTillCompletion returns a BuildRun that have a CompletionTime set. +// If the timeout is reached or it fails when retrieving the BuildRun it will +// stop polling and return +func (t *TestBuild) GetBRTillCompletion(name string) (*v1beta1.BuildRun, error) { + + var ( + pollBRTillCompletion = func() (bool, error) { + + bInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + buildRun, err := bInterface.Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return false, err + } + if buildRun.Status.CompletionTime != nil { + return true, nil + } + + return false, nil + } + ) + + brInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + err := wait.PollImmediate(t.Interval, t.TimeOut, pollBRTillCompletion) + if err != nil { + return nil, err + } + + return brInterface.Get(context.TODO(), name, metav1.GetOptions{}) +} + +// GetBRTillNotFound waits for the buildrun to get deleted. It returns an error if BuildRun is not found +func (t *TestBuild) GetBRTillNotFound(name string, interval time.Duration, timeout time.Duration) (*v1beta1.BuildRun, error) { + + var ( + GetBRTillNotFound = func() (bool, error) { + + bInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + _, err := bInterface.Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil && apierrors.IsNotFound(err) { + return true, err + } + return false, nil + } + ) + + brInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + err := wait.PollImmediate(interval, timeout, GetBRTillNotFound) + if err != nil { + return nil, err + } + + return brInterface.Get(context.TODO(), name, metav1.GetOptions{}) +} + +// GetBRTillNotOwner returns a BuildRun that has not an owner. +// If the timeout is reached or it fails when retrieving the BuildRun it will +// stop polling and return +func (t *TestBuild) GetBRTillNotOwner(name string, owner string) (*v1beta1.BuildRun, error) { + + brInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + var ( + pollBRTillNotOwner = func() (bool, error) { + + buildRun, err := brInterface.Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return false, err + } + + for _, ownerReference := range buildRun.OwnerReferences { + if ownerReference.Name == owner { + return false, nil + } + } + + return true, nil + } + ) + + if err := wait.PollImmediate(t.Interval, t.TimeOut, pollBRTillNotOwner); err != nil { + return nil, err + } + + return brInterface.Get(context.TODO(), name, metav1.GetOptions{}) +} + +// GetBRTillOwner returns a BuildRun that has an owner. +// If the timeout is reached or it fails when retrieving the BuildRun it will +// stop polling and return +func (t *TestBuild) GetBRTillOwner(name string, owner string) (*v1beta1.BuildRun, error) { + + brInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + var ( + pollBRTillOwner = func() (bool, error) { + + buildRun, err := brInterface.Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return false, err + } + + for _, ownerReference := range buildRun.OwnerReferences { + if ownerReference.Name == owner { + return true, nil + } + } + + return false, nil + } + ) + + if err := wait.PollImmediate(t.Interval, t.TimeOut, pollBRTillOwner); err != nil { + return nil, err + } + + return brInterface.Get(context.TODO(), name, metav1.GetOptions{}) +} + +// GetBRTillStartTime returns a BuildRun that have a StartTime set. +// If the timeout is reached or it fails when retrieving the BuildRun it will +// stop polling and return +func (t *TestBuild) GetBRTillStartTime(name string) (*v1beta1.BuildRun, error) { + + var ( + pollBRTillCompletion = func() (bool, error) { + + bInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + buildRun, err := bInterface.Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return false, err + } + if buildRun.Status.StartTime != nil { + return true, nil + } + + // early exit + if buildRun.Status.CompletionTime != nil { + if buildRunJSON, err := json.Marshal(buildRun); err == nil { + return false, fmt.Errorf("buildrun is completed: %s", buildRunJSON) + } + + return false, fmt.Errorf("buildrun is completed") + } + + return false, nil + } + ) + + brInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + err := wait.PollImmediate(t.Interval, t.TimeOut, pollBRTillCompletion) + if err != nil { + return nil, err + } + + return brInterface.Get(context.TODO(), name, metav1.GetOptions{}) +} + +// GetBRTillDesiredReason polls until a BuildRun gets a particular Reason +// it exit if an error happens or the timeout is reached +func (t *TestBuild) GetBRTillDesiredReason(buildRunname string, reason string) (currentReason string, err error) { + err = wait.PollImmediate(t.Interval, t.TimeOut, func() (bool, error) { + currentReason, err = t.GetBRReason(buildRunname) + if err != nil { + return false, err + } + if currentReason == reason { + return true, nil + } + + return false, nil + }) + + return +} + +// GetBRTillDeletion polls until a BuildRun is not found, it returns +// if a timeout is reached +func (t *TestBuild) GetBRTillDeletion(name string) (bool, error) { + + var ( + pollBRTillCompletion = func() (bool, error) { + + bInterface := t.BuildClientSet.ShipwrightV1beta1().BuildRuns(t.Namespace) + + _, err := bInterface.Get(context.TODO(), name, metav1.GetOptions{}) + if apierrors.IsNotFound(err) { + return true, nil + } + return false, nil + } + ) + + err := wait.PollImmediate(t.Interval, t.TimeOut, pollBRTillCompletion) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/test/utils/v1beta1/builds.go b/test/utils/v1beta1/builds.go new file mode 100644 index 000000000..e6e5704b4 --- /dev/null +++ b/test/utils/v1beta1/builds.go @@ -0,0 +1,149 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + "strings" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + + "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +// This class is intended to host all CRUD calls for testing Build CRDs resources + +// CreateBuild generates a Build on the current test namespace +func (t *TestBuild) CreateBuild(build *v1beta1.Build) error { + bInterface := t.BuildClientSet.ShipwrightV1beta1().Builds(t.Namespace) + + _, err := bInterface.Create(context.TODO(), build, metav1.CreateOptions{}) + return err +} + +// DeleteBuild deletes a Build on the desired namespace +func (t *TestBuild) DeleteBuild(name string) error { + bInterface := t.BuildClientSet.ShipwrightV1beta1().Builds(t.Namespace) + + err := bInterface.Delete(context.TODO(), name, metav1.DeleteOptions{}) + + return err +} + +// GetBuild returns a Build based on name +// Deprecated: Use LookupBuild instead +func (t *TestBuild) GetBuild(name string) (*v1beta1.Build, error) { + return t.BuildClientSet.ShipwrightV1beta1(). + Builds(t.Namespace).Get(context.TODO(), name, metav1.GetOptions{}) +} + +// ListBuilds returns existing Builds from the desired namespace +func (t *TestBuild) ListBuilds(namespace string) (*v1beta1.BuildList, error) { + return t.BuildClientSet.ShipwrightV1beta1().Builds(namespace).List(t.Context, metav1.ListOptions{}) +} + +// PatchBuild patches an existing Build using the merge patch type +func (t *TestBuild) PatchBuild(buildName string, data []byte) (*v1beta1.Build, error) { + return t.PatchBuildWithPatchType(buildName, data, types.MergePatchType) +} + +// PatchBuildWithPatchType patches an existing Build and allows specifying the patch type +func (t *TestBuild) PatchBuildWithPatchType(buildName string, data []byte, pt types.PatchType) (*v1beta1.Build, error) { + bInterface := t.BuildClientSet.ShipwrightV1beta1().Builds(t.Namespace) + b, err := bInterface.Patch(context.TODO(), buildName, pt, data, metav1.PatchOptions{}) + if err != nil { + return nil, err + } + return b, nil +} + +// GetBuildTillValidation polls until a Build gets a validation and updates +// it´s registered field. If timeout is reached or an error is found, it will +// return with an error +func (t *TestBuild) GetBuildTillValidation(name string) (build *v1beta1.Build, err error) { + err = wait.PollImmediate(t.Interval, t.TimeOut, func() (bool, error) { + build, err = t.LookupBuild(types.NamespacedName{Namespace: t.Namespace, Name: name}) + if err != nil && !apierrors.IsNotFound(err) { + return false, err + } + + // TODO: we might improve the conditional here + if build.Status.Registered != nil && *build.Status.Registered != "" { + return true, nil + } + + return false, nil + }) + + return +} + +// GetBuildTillRegistration polls until a Build gets a desired validation and updates +// it´s registered field. If timeout is reached or an error is found, it will +// return with an error +func (t *TestBuild) GetBuildTillRegistration(name string, condition corev1.ConditionStatus) (*v1beta1.Build, error) { + + var ( + pollBuildTillRegistration = func() (bool, error) { + + bInterface := t.BuildClientSet.ShipwrightV1beta1().Builds(t.Namespace) + + buildRun, err := bInterface.Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return false, err + } + // TODO: we might improve the conditional here + if buildRun.Status.Registered != nil && *buildRun.Status.Registered == condition { + return true, nil + } + + return false, nil + } + ) + + brInterface := t.BuildClientSet.ShipwrightV1beta1().Builds(t.Namespace) + + if err := wait.PollImmediate(t.Interval, t.TimeOut, pollBuildTillRegistration); err != nil { + return nil, err + } + + return brInterface.Get(context.TODO(), name, metav1.GetOptions{}) +} + +// GetBuildTillMessageContainsSubstring polls until a Build message contains the desired +// substring value and updates it´s registered field. If timeout is reached or an error is found, +// it will return with an error +func (t *TestBuild) GetBuildTillMessageContainsSubstring(name string, partOfMessage string) (*v1beta1.Build, error) { + + var ( + pollBuildTillMessageContainsSubString = func() (bool, error) { + + bInterface := t.BuildClientSet.ShipwrightV1beta1().Builds(t.Namespace) + + buildRun, err := bInterface.Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return false, err + } + + if strings.Contains(*buildRun.Status.Message, partOfMessage) { + return true, nil + } + + return false, nil + } + ) + + brInterface := t.BuildClientSet.ShipwrightV1beta1().Builds(t.Namespace) + + if err := wait.PollImmediate(t.Interval, t.TimeOut, pollBuildTillMessageContainsSubString); err != nil { + return nil, err + } + + return brInterface.Get(context.TODO(), name, metav1.GetOptions{}) +} diff --git a/test/utils/v1beta1/buildstrategies.go b/test/utils/v1beta1/buildstrategies.go new file mode 100644 index 000000000..1c480bc97 --- /dev/null +++ b/test/utils/v1beta1/buildstrategies.go @@ -0,0 +1,33 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +// This class is intended to host all CRUD calls for testing BuildStrategy CRDs resources + +// CreateBuildStrategy generates a BuildStrategy on the current test namespace +func (t *TestBuild) CreateBuildStrategy(bs *v1beta1.BuildStrategy) error { + bsInterface := t.BuildClientSet.ShipwrightV1beta1().BuildStrategies(t.Namespace) + + _, err := bsInterface.Create(context.TODO(), bs, metav1.CreateOptions{}) + if err != nil { + return err + } + return nil +} + +// DeleteBuildStrategy deletes a BuildStrategy on the current test namespace +func (t *TestBuild) DeleteBuildStrategy(name string) error { + bsInterface := t.BuildClientSet.ShipwrightV1beta1().BuildStrategies(t.Namespace) + + return bsInterface.Delete(context.TODO(), name, metav1.DeleteOptions{}) +} diff --git a/test/utils/v1beta1/clusterbuildstrategies.go b/test/utils/v1beta1/clusterbuildstrategies.go new file mode 100644 index 000000000..bf8ffbc29 --- /dev/null +++ b/test/utils/v1beta1/clusterbuildstrategies.go @@ -0,0 +1,37 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +// This class is intended to host all CRUD calls for testing ClusterBuildStrategy CRDs resources + +// CreateClusterBuildStrategy generates a ClusterBuildStrategy on the current test namespace +func (t *TestBuild) CreateClusterBuildStrategy(cbs *v1beta1.ClusterBuildStrategy) error { + cbsInterface := t.BuildClientSet.ShipwrightV1beta1().ClusterBuildStrategies() + + _, err := cbsInterface.Create(context.TODO(), cbs, metav1.CreateOptions{}) + if err != nil { + return err + } + return nil +} + +// DeleteClusterBuildStrategy deletes a ClusterBuildStrategy on the desired namespace +func (t *TestBuild) DeleteClusterBuildStrategy(name string) error { + cbsInterface := t.BuildClientSet.ShipwrightV1beta1().ClusterBuildStrategies() + + err := cbsInterface.Delete(context.TODO(), name, metav1.DeleteOptions{}) + if err != nil { + return err + } + return nil +} diff --git a/test/utils/v1beta1/configmaps.go b/test/utils/v1beta1/configmaps.go new file mode 100644 index 000000000..748bbd803 --- /dev/null +++ b/test/utils/v1beta1/configmaps.go @@ -0,0 +1,33 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// This class is intended to host all CRUD calls for testing configmap primitive resources + +// CreateConfigMap generates a ConfigMap on the current test namespace +func (t *TestBuild) CreateConfigMap(configMap *corev1.ConfigMap) error { + client := t.Clientset.CoreV1().ConfigMaps(t.Namespace) + _, err := client.Create(context.TODO(), configMap, metav1.CreateOptions{}) + if err != nil { + return err + } + return nil +} + +// DeleteConfigMap removes the desired configMap +func (t *TestBuild) DeleteConfigMap(name string) error { + client := t.Clientset.CoreV1().ConfigMaps(t.Namespace) + if err := client.Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil { + return err + } + return nil +} diff --git a/test/utils/v1beta1/controllers.go b/test/utils/v1beta1/controllers.go new file mode 100644 index 000000000..539dd58f0 --- /dev/null +++ b/test/utils/v1beta1/controllers.go @@ -0,0 +1,41 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "sigs.k8s.io/controller-runtime/pkg/manager" + + buildconfig "github.com/shipwright-io/build/pkg/config" + "github.com/shipwright-io/build/pkg/controller" +) + +// StartBuildControllers initialize an operator as if being call from main, +// but it disables the prometheus metrics and leader election. This intended +// to for testing. +func (t *TestBuild) StartBuildControllers() error { + c := buildconfig.NewDefaultConfig() + + // read configuration from environment variables, especially the GIT_CONTAINER_IMAGE + c.SetConfigFromEnv() + + mgr, err := controller.NewManager(t.Context, c, t.KubeConfig, manager.Options{ + Namespace: t.Namespace, + LeaderElection: false, + MetricsBindAddress: "0", + }) + if err != nil { + return err + } + + go func() { + // set stopChan with the channel for future closing + err := mgr.Start(t.Context) + if err != nil { + panic(err) + } + }() + + return nil +} diff --git a/test/utils/environment.go b/test/utils/v1beta1/environment.go similarity index 92% rename from test/utils/environment.go rename to test/utils/v1beta1/environment.go index 987aaa8c5..df541a02f 100644 --- a/test/utils/environment.go +++ b/test/utils/v1beta1/environment.go @@ -24,7 +24,8 @@ import ( buildClient "github.com/shipwright-io/build/pkg/client/clientset/versioned" "github.com/shipwright-io/build/pkg/ctxlog" - "github.com/shipwright-io/build/test" + test "github.com/shipwright-io/build/test/v1beta1_samples" + // from https://github.com/kubernetes/client-go/issues/345 ) var ( @@ -41,11 +42,11 @@ type TestBuild struct { Interval time.Duration TimeOut time.Duration KubeConfig *rest.Config - Clientset kubernetes.Interface + Clientset *kubernetes.Clientset Namespace string StopBuildControllers context.CancelFunc - BuildClientSet buildClient.Interface - PipelineClientSet tektonClient.Interface + BuildClientSet *buildClient.Clientset + PipelineClientSet *tektonClient.Clientset ControllerRuntimeClient client.Client Catalog test.Catalog Context context.Context diff --git a/test/utils/v1beta1/gomega.go b/test/utils/v1beta1/gomega.go new file mode 100644 index 000000000..db7bc8bdb --- /dev/null +++ b/test/utils/v1beta1/gomega.go @@ -0,0 +1,142 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "fmt" + "net/http" + "reflect" + + "github.com/onsi/gomega/format" + "github.com/onsi/gomega/types" +) + +type containNamedElementMatcher struct { + Name string +} + +func (matcher *containNamedElementMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to contain element with name", matcher.Name) +} + +func (matcher *containNamedElementMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to contain element with name", matcher.Name) +} + +func (matcher *containNamedElementMatcher) Match(actual interface{}) (success bool, err error) { + if actual == nil { + return false, nil + } + + kind := reflect.TypeOf(actual).Kind() + if kind == reflect.Array || kind == reflect.Slice { + value := reflect.ValueOf(actual) + + for i := 0; i < value.Len(); i++ { + vItem := value.Index(i) + vName := vItem.FieldByName("Name") + if !vName.IsZero() && matcher.Name == vName.String() { + return true, nil + } + } + } + + return false, nil +} + +// ContainNamedElement can be applied for an array or slice of objects which have a Name field, to check if any item has a matching name +func ContainNamedElement(name string) types.GomegaMatcher { + return &containNamedElementMatcher{ + Name: name, + } +} + +type containNamedWithValueElementMatcher struct { + Name string + Value string +} + +func (matcher *containNamedWithValueElementMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to contain element with name and value", fmt.Sprintf("%s=%s", matcher.Name, matcher.Value)) +} + +func (matcher *containNamedWithValueElementMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to contain element with name and value", fmt.Sprintf("%s=%s", matcher.Name, matcher.Value)) +} + +func (matcher *containNamedWithValueElementMatcher) Match(actual interface{}) (success bool, err error) { + if actual == nil { + return false, nil + } + + kind := reflect.TypeOf(actual).Kind() + if kind == reflect.Array || kind == reflect.Slice { + value := reflect.ValueOf(actual) + + for i := 0; i < value.Len(); i++ { + vItem := value.Index(i) + vName := vItem.FieldByName("Name") + if !vName.IsZero() && matcher.Name == vName.String() { + vValue := vItem.FieldByName("Value") + if !vValue.IsZero() && matcher.Value == vValue.String() { + return true, nil + } + } + } + } + + return false, nil +} + +// ContainNamedElementWithValue can be applied for an array or slice of objects which have a Name and Value field, to check if any item has a matching name and value +func ContainNamedElementWithValue(name string, value string) types.GomegaMatcher { + return &containNamedWithValueElementMatcher{ + Name: name, + Value: value, + } +} + +type returnMatcher struct { + actualStatusCode int + expectedStatusCode int +} + +func (matcher *returnMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(matcher.expectedStatusCode, fmt.Sprintf("to be the HTTP response for %s, but received", actual), matcher.actualStatusCode) +} + +func (matcher *returnMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(matcher.expectedStatusCode, fmt.Sprintf("to not be the HTTP response for %s, but received", actual), matcher.actualStatusCode) +} + +func (matcher *returnMatcher) Match(actual interface{}) (success bool, err error) { + if actual == nil { + return false, nil + } + + kind := reflect.TypeOf(actual).Kind() + if kind == reflect.String { + url := reflect.ValueOf(actual).String() + + // #nosec:G107 test code + resp, err := http.Get(url) + if err != nil { + return false, err + } + + matcher.actualStatusCode = resp.StatusCode + + return resp.StatusCode == matcher.expectedStatusCode, nil + } + + return false, nil +} + +// Return can be applied for a string, it will call the URL and check the status code +func Return(statusCode int) types.GomegaMatcher { + return &returnMatcher{ + expectedStatusCode: statusCode, + } +} diff --git a/test/utils/v1beta1/image.go b/test/utils/v1beta1/image.go new file mode 100644 index 000000000..4b900cb72 --- /dev/null +++ b/test/utils/v1beta1/image.go @@ -0,0 +1,115 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "encoding/json" + "fmt" + "log" + "strings" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + containerreg "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/remote" + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" + "k8s.io/apimachinery/pkg/types" + + . "github.com/onsi/gomega" +) + +func getImageURL(buildRun *buildv1beta1.BuildRun) string { + image := "" + if buildRun.Spec.Output.Image != "" { + image = buildRun.Spec.Output.Image + } else { + image = buildRun.Status.BuildSpec.Output.Image + } + + if buildRun.Status.Output != nil && buildRun.Status.Output.Digest != "" { + image = fmt.Sprintf("%s@%s", image, buildRun.Status.Output.Digest) + } + + // In the GitHub action, we are using a registry inside the cluster to + // push the image created by `buildRun`. The registry inside the cluster + // is not directly accessible from the local, so that we have mapped + // the cluster registry port to the local system + // by providing `test/kind/config.yaml` config to the kind + return strings.Replace(image, "registry.registry.svc.cluster.local", "localhost", 1) +} + +// GetImage loads the image manifest for the image produced by a BuildRun +func (t *TestBuild) GetImage(buildRun *buildv1beta1.BuildRun) containerreg.Image { + ref, err := name.ParseReference(getImageURL(buildRun)) + Expect(err).ToNot(HaveOccurred()) + + img, err := remote.Image(ref, remote.WithAuth(t.getRegistryAuthentication(buildRun, ref))) + Expect(err).ToNot(HaveOccurred()) + + return img +} + +func (t *TestBuild) getRegistryAuthentication( + buildRun *buildv1beta1.BuildRun, + ref name.Reference, +) authn.Authenticator { + secretName := "" + if buildRun.Spec.Output != nil && buildRun.Spec.Output.PushSecret != nil { + secretName = *buildRun.Spec.Output.PushSecret + } else if buildRun.Status.BuildSpec.Output.PushSecret != nil { + secretName = *buildRun.Status.BuildSpec.Output.PushSecret + } + + // In case no secret is mounted, use anonymous + if secretName == "" { + log.Println("No access credentials provided, using anonymous mode") + return authn.Anonymous + } + + secret, err := t.LookupSecret( + types.NamespacedName{ + Namespace: buildRun.Namespace, + Name: secretName, + }, + ) + Expect(err).ToNot(HaveOccurred(), "Error retrieving registry secret") + + type auth struct { + Auths map[string]authn.AuthConfig `json:"auths,omitempty"` + } + + var authConfig auth + + Expect(json.Unmarshal(secret.Data[".dockerconfigjson"], &authConfig)).ToNot(HaveOccurred(), "Error parsing secrets docker config") + + // Look-up the respective registry server inside the credentials + registryName := ref.Context().RegistryStr() + if registryName == name.DefaultRegistry { + registryName = authn.DefaultAuthKey + } + + return authn.FromConfig(authConfig.Auths[registryName]) +} + +// ValidateImagePlatformsExist that the image produced by a BuildRun exists for a set of platforms +func (t *TestBuild) ValidateImagePlatformsExist(buildRun *buildv1beta1.BuildRun, expectedPlatforms []containerreg.Platform) { + ref, err := name.ParseReference(getImageURL(buildRun)) + Expect(err).ToNot(HaveOccurred()) + + for _, expectedPlatform := range expectedPlatforms { + _, err := remote.Image(ref, remote.WithAuth(t.getRegistryAuthentication(buildRun, ref)), remote.WithPlatform(expectedPlatform)) + Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("Failed to validate %s/%s", expectedPlatform.OS, expectedPlatform.Architecture)) + } +} + +// ValidateImageDigest ensures that an image digest is set in the BuildRun status and that this digest is pointing to an image +func (t *TestBuild) ValidateImageDigest(buildRun *buildv1beta1.BuildRun) { + // Verify that the status contains a digest + Expect(buildRun.Status.Output).NotTo(BeNil(), ".status.output is nil") + Expect(buildRun.Status.Output.Digest).NotTo(Equal(""), ".status.output.digest is empty") + + // Verify that the digest is valid by retrieving the image manifest + t.GetImage(buildRun) +} diff --git a/test/utils/v1beta1/lookup.go b/test/utils/v1beta1/lookup.go new file mode 100644 index 000000000..595c9a6e5 --- /dev/null +++ b/test/utils/v1beta1/lookup.go @@ -0,0 +1,151 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "fmt" + "strings" + "time" + + pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + + buildv1beta1 "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +func (t *TestBuild) LookupSecret(entity types.NamespacedName) (*corev1.Secret, error) { + result, err := lookupRuntimeObject(func() (runtime.Object, error) { + return t.Clientset. + CoreV1(). + Secrets(entity.Namespace). + Get(t.Context, entity.Name, metav1.GetOptions{}) + }) + + return result.(*corev1.Secret), err +} + +func (t *TestBuild) LookupPod(entity types.NamespacedName) (*corev1.Pod, error) { + result, err := lookupRuntimeObject(func() (runtime.Object, error) { + return t.Clientset. + CoreV1(). + Pods(entity.Namespace). + Get(t.Context, entity.Name, metav1.GetOptions{}) + }) + + return result.(*corev1.Pod), err +} + +func (t *TestBuild) LookupBuild(entity types.NamespacedName) (*buildv1beta1.Build, error) { + result, err := lookupRuntimeObject(func() (runtime.Object, error) { + return t.BuildClientSet.ShipwrightV1beta1(). + Builds(entity.Namespace).Get(t.Context, entity.Name, metav1.GetOptions{}) + }) + + return result.(*buildv1beta1.Build), err +} + +func (t *TestBuild) LookupBuildRun(entity types.NamespacedName) (*buildv1beta1.BuildRun, error) { + result, err := lookupRuntimeObject(func() (runtime.Object, error) { + return t.BuildClientSet.ShipwrightV1beta1(). + BuildRuns(entity.Namespace).Get(t.Context, entity.Name, metav1.GetOptions{}) + }) + + return result.(*buildv1beta1.BuildRun), err +} + +func (t *TestBuild) LookupTaskRun(entity types.NamespacedName) (*pipelinev1beta1.TaskRun, error) { + result, err := lookupRuntimeObject(func() (runtime.Object, error) { + return t.PipelineClientSet. + TektonV1beta1(). + TaskRuns(entity.Namespace). + Get(t.Context, entity.Name, metav1.GetOptions{}) + }) + + return result.(*pipelinev1beta1.TaskRun), err +} + +func (t *TestBuild) LookupTaskRunUsingBuildRun(buildRun *buildv1beta1.BuildRun) (*pipelinev1beta1.TaskRun, error) { + if buildRun == nil { + return nil, fmt.Errorf("no BuildRun specified to lookup TaskRun") + } + + if buildRun.Status.TaskRunName != nil { + return t.LookupTaskRun(types.NamespacedName{Namespace: buildRun.Namespace, Name: *buildRun.Status.TaskRunName}) + } + + tmp, err := lookupRuntimeObject(func() (runtime.Object, error) { + return t.PipelineClientSet. + TektonV1beta1(). + TaskRuns(buildRun.Namespace). + List(t.Context, metav1.ListOptions{ + LabelSelector: labels.SelectorFromSet( + map[string]string{ + buildv1beta1.LabelBuildRun: buildRun.Name, + }).String(), + }) + }) + + if err != nil { + return nil, err + } + + var taskRunList = tmp.(*pipelinev1beta1.TaskRunList) + switch len(taskRunList.Items) { + case 0: + return nil, fmt.Errorf("no TaskRun found for BuildRun %s/%s", buildRun.Namespace, buildRun.Name) + + case 1: + return &taskRunList.Items[0], nil + + default: + return nil, fmt.Errorf("multiple TaskRuns found for BuildRun %s/%s, which should not have happened", buildRun.Namespace, buildRun.Name) + } +} + +func (t *TestBuild) LookupServiceAccount(entity types.NamespacedName) (*corev1.ServiceAccount, error) { + result, err := lookupRuntimeObject(func() (runtime.Object, error) { + return t.Clientset. + CoreV1(). + ServiceAccounts(entity.Namespace). + Get(t.Context, entity.Name, metav1.GetOptions{}) + }) + + return result.(*corev1.ServiceAccount), err +} + +func lookupRuntimeObject(f func() (runtime.Object, error)) (result runtime.Object, err error) { + err = wait.PollImmediate(4*time.Second, 60*time.Second, func() (bool, error) { + result, err = f() + if err != nil { + // check if we have an error that we want to retry + if isRetryableError(err) { + return false, nil + } + + return true, err + } + + // successful call + return true, nil + }) + + return +} + +func isRetryableError(err error) bool { + return apierrors.IsServerTimeout(err) || + apierrors.IsTimeout(err) || + apierrors.IsTooManyRequests(err) || + apierrors.IsInternalError(err) || + err.Error() == "etcdserver: request timed out" || + err.Error() == "rpc error: code = Unavailable desc = transport is closing" || + strings.Contains(err.Error(), "net/http: request canceled while waiting for connection") +} diff --git a/test/utils/v1beta1/namespaces.go b/test/utils/v1beta1/namespaces.go new file mode 100644 index 000000000..e5ad5bf00 --- /dev/null +++ b/test/utils/v1beta1/namespaces.go @@ -0,0 +1,73 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + "time" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" +) + +// This class is intended to host all CRUD calls for Namespace primitive resources + +// CreateNamespace generates a Namespace with the current test name +func (t *TestBuild) CreateNamespace() error { + client := t.Clientset.CoreV1().Namespaces() + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: t.Namespace, + }, + } + _, err := client.Create(context.TODO(), ns, metav1.CreateOptions{}) + if err != nil { + return err + } + + // wait for the default service account to exist + pollServiceAccount := func() (bool, error) { + + serviceAccountInterface := t.Clientset.CoreV1().ServiceAccounts(t.Namespace) + + _, err := serviceAccountInterface.Get(context.TODO(), "default", metav1.GetOptions{}) + if err != nil { + if !apierrors.IsNotFound(err) { + return false, err + } + return false, nil + } + + return true, nil + } + + return wait.PollImmediate(t.Interval, t.TimeOut, pollServiceAccount) +} + +// DeleteNamespace deletes the namespace with the current test name +func (t *TestBuild) DeleteNamespace() error { + client := t.Clientset.CoreV1().Namespaces() + + if err := client.Delete(context.TODO(), t.Namespace, metav1.DeleteOptions{}); err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return err + } + + // wait for the namespace to be deleted + pollNamespace := func() (bool, error) { + + if _, err := client.Get(context.TODO(), t.Namespace, metav1.GetOptions{}); err != nil && apierrors.IsNotFound(err) { + return true, nil + } + + return false, nil + } + + return wait.PollImmediate(t.Interval, 5*time.Second, pollNamespace) +} diff --git a/test/utils/v1beta1/secrets.go b/test/utils/v1beta1/secrets.go new file mode 100644 index 000000000..9bd8016b9 --- /dev/null +++ b/test/utils/v1beta1/secrets.go @@ -0,0 +1,50 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +// This class is intended to host all CRUD calls for testing secrets primitive resources + +// CreateSecret generates a Secret on the current test namespace +func (t *TestBuild) CreateSecret(secret *corev1.Secret) error { + client := t.Clientset.CoreV1().Secrets(t.Namespace) + _, err := client.Create(context.TODO(), secret, metav1.CreateOptions{}) + if err != nil { + return err + } + return nil +} + +// DeleteSecret removes the desired secret +func (t *TestBuild) DeleteSecret(name string) error { + client := t.Clientset.CoreV1().Secrets(t.Namespace) + if err := client.Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil { + return err + } + return nil +} + +// PatchSecret patches a secret based on name and with the provided data. +// It used the merge type strategy +func (t *TestBuild) PatchSecret(name string, data []byte) (*corev1.Secret, error) { + return t.PatchSecretWithPatchType(name, data, types.MergePatchType) +} + +// PatchSecretWithPatchType patches a secret with a desire data and patch strategy +func (t *TestBuild) PatchSecretWithPatchType(name string, data []byte, pt types.PatchType) (*corev1.Secret, error) { + secInterface := t.Clientset.CoreV1().Secrets(t.Namespace) + b, err := secInterface.Patch(context.TODO(), name, pt, data, metav1.PatchOptions{}) + if err != nil { + return nil, err + } + return b, nil +} diff --git a/test/utils/v1beta1/service_accounts.go b/test/utils/v1beta1/service_accounts.go new file mode 100644 index 000000000..27de19f5e --- /dev/null +++ b/test/utils/v1beta1/service_accounts.go @@ -0,0 +1,44 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// This class is intended to host all CRUD calls for testing secrets primitive resources + +// CreateSAFromName creates a simple ServiceAccount with the provided name if it does not exist. +func (t *TestBuild) CreateSAFromName(saName string) error { + client := t.Clientset.CoreV1().ServiceAccounts(t.Namespace) + _, err := client.Get(context.TODO(), saName, metav1.GetOptions{}) + // If the service account already exists, no error is returned + if err == nil { + return nil + } + if !apierrors.IsNotFound(err) { + return err + } + _, err = client.Create(context.TODO(), &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: saName, + }}, metav1.CreateOptions{}) + return err +} + +// GetSA retrieves an existing service-account by name +// Deprecated: Use LookupServiceAccount instead. +func (t *TestBuild) GetSA(saName string) (*corev1.ServiceAccount, error) { + client := t.Clientset.CoreV1().ServiceAccounts(t.Namespace) + sa, err := client.Get(context.TODO(), saName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return sa, nil +} diff --git a/test/utils/v1beta1/taskruns.go b/test/utils/v1beta1/taskruns.go new file mode 100644 index 000000000..aef9a5391 --- /dev/null +++ b/test/utils/v1beta1/taskruns.go @@ -0,0 +1,115 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + "errors" + "fmt" + + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "knative.dev/pkg/apis" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" +) + +// This class is intended to host all CRUD calls for testing TaskRuns CRDs resources + +// GetTaskRunFromBuildRun retrieves an owned TaskRun based on the BuildRunName +func (t *TestBuild) GetTaskRunFromBuildRun(buildRunName string) (*v1beta1.TaskRun, error) { + taskRunLabelSelector := fmt.Sprintf("buildrun.shipwright.io/name=%s", buildRunName) + + trInterface := t.PipelineClientSet.TektonV1beta1().TaskRuns(t.Namespace) + + trList, err := trInterface.List(context.TODO(), metav1.ListOptions{ + LabelSelector: taskRunLabelSelector, + }) + if err != nil { + return nil, err + } + + if len(trList.Items) != 1 { + return nil, fmt.Errorf("failed to find an owned TaskRun based on a Buildrun %s/%s name", t.Namespace, buildRunName) + } + + return &trList.Items[0], nil +} + +// UpdateTaskRun applies changes to a TaskRun object +func (t *TestBuild) UpdateTaskRun(name string, apply func(tr *v1beta1.TaskRun)) (*v1beta1.TaskRun, error) { + var tr *v1beta1.TaskRun + var err error + for i := 0; i < 5; i++ { + tr, err = t.LookupTaskRun(types.NamespacedName{ + Namespace: t.Namespace, + Name: name, + }) + if err != nil { + return nil, err + } + + apply(tr) + + tr, err = t.PipelineClientSet.TektonV1beta1().TaskRuns(t.Namespace).Update(context.TODO(), tr, metav1.UpdateOptions{}) + if err == nil { + return tr, nil + } + // retry the famous ""Operation cannot be fulfilled on taskruns.tekton.dev \"buildrun-test-build-225-xkw6k\": the object has been modified; please apply your changes to the latest version and try again" error + if !apierrors.IsConflict(err) { + return nil, err + } + } + + return nil, err +} + +// GetTRReason returns the Reason of the Succeeded condition +// of an existing TaskRun based on a BuildRun name +func (t *TestBuild) GetTRReason(buildRunName string) (string, error) { + tr, err := t.GetTaskRunFromBuildRun(buildRunName) + if err != nil { + return "", err + } + + trCondition := tr.Status.GetCondition(apis.ConditionSucceeded) + if trCondition != nil { + return trCondition.Reason, nil + } + + return "", errors.New("foo") +} + +// GetTRTillDesiredReason polls until a TaskRun matches a desired Reason +// or it exits if an error happen or a timeout is reach. +func (t *TestBuild) GetTRTillDesiredReason(buildRunName string, reason string) (trReason string, err error) { + err = wait.PollImmediate(t.Interval, t.TimeOut, func() (bool, error) { + trReason, err = t.GetTRReason(buildRunName) + if err != nil { + return false, err + } + + if trReason == reason { + return true, nil + } + + return false, nil + }) + + return +} + +// DeleteTR deletes a TaskRun from a desired namespace +func (t *TestBuild) DeleteTR(name string) error { + trInterface := t.PipelineClientSet.TektonV1beta1().TaskRuns(t.Namespace) + + if err := trInterface.Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil { + return err + } + + return nil +} diff --git a/test/utils/v1beta1/webhook.go b/test/utils/v1beta1/webhook.go new file mode 100644 index 000000000..731a7133e --- /dev/null +++ b/test/utils/v1beta1/webhook.go @@ -0,0 +1,110 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + "crypto/tls" + "net/http" + "time" + + "github.com/shipwright-io/build/pkg/webhook/conversion" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +func StartBuildWebhook() *http.Server { + mux := http.NewServeMux() + mux.HandleFunc("/convert", conversion.CRDConvertHandler(context.Background())) + mux.HandleFunc("/health", health) + + webhookServer := &http.Server{ + Addr: ":30443", + Handler: mux, + ReadHeaderTimeout: 32 * time.Second, + IdleTimeout: time.Second, + TLSConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{tls.CurveP256, tls.CurveP384, tls.X25519}, + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + }, + }, + } + + // start server + go func() { + defer ginkgo.GinkgoRecover() + + if err := webhookServer.ListenAndServeTLS("/tmp/server-cert.pem", "/tmp/server-key.pem"); err != nil { + if err != http.ErrServerClosed { + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + } + } + }() + + client := &http.Client{ + Transport: &http.Transport{ + IdleConnTimeout: 5 * time.Second, + ResponseHeaderTimeout: 5 * time.Second, + // #nosec:G402 test code + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + TLSHandshakeTimeout: 5 * time.Second, + }, + } + + gomega.Eventually(func() int { + r, err := client.Get("https://localhost:30443/health") + if err != nil { + return 0 + } + if r != nil { + return r.StatusCode + } + return 0 + }).WithTimeout(10 * time.Second).Should(gomega.Equal(http.StatusNoContent)) + + return webhookServer +} + +func StopBuildWebhook(webhookServer *http.Server) { + err := webhookServer.Close() + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + + client := &http.Client{ + Transport: &http.Transport{ + IdleConnTimeout: 5 * time.Second, + ResponseHeaderTimeout: 5 * time.Second, + // #nosec:G402 test code + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + TLSHandshakeTimeout: 5 * time.Second, + }, + } + + gomega.Eventually(func() int { + r, err := client.Get("https://localhost:30443/health") + if err != nil { + return 0 + } + if r != nil { + return r.StatusCode + } + return 0 + }).WithTimeout(10 * time.Second).Should(gomega.Equal(0)) +} + +func health(resp http.ResponseWriter, _ *http.Request) { + resp.WriteHeader(http.StatusNoContent) +} diff --git a/test/build_samples.go b/test/v1alpha1_samples/build_samples.go similarity index 99% rename from test/build_samples.go rename to test/v1alpha1_samples/build_samples.go index 9351f4adb..48e6b1fab 100644 --- a/test/build_samples.go +++ b/test/v1alpha1_samples/build_samples.go @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package test +package testalpha // MinimalBuildahBuildWithEnvVars defines a simple // Build with a source, strategy, and env vars diff --git a/test/buildrun_samples.go b/test/v1alpha1_samples/buildrun_samples.go similarity index 99% rename from test/buildrun_samples.go rename to test/v1alpha1_samples/buildrun_samples.go index 84a0964e1..02e505104 100644 --- a/test/buildrun_samples.go +++ b/test/v1alpha1_samples/buildrun_samples.go @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package test +package testalpha // MinimalBuildahBuildRunWithEnvVars defines a simple // BuildRun with a referenced Build and env vars diff --git a/test/buildstrategy_samples.go b/test/v1alpha1_samples/buildstrategy_samples.go similarity index 99% rename from test/buildstrategy_samples.go rename to test/v1alpha1_samples/buildstrategy_samples.go index e7b7b55c1..4f99f8445 100644 --- a/test/buildstrategy_samples.go +++ b/test/v1alpha1_samples/buildstrategy_samples.go @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package test +package testalpha // MinimalBuildahBuildStrategy defines a // BuildStrategy for Buildah with two steps diff --git a/test/catalog.go b/test/v1alpha1_samples/catalog.go similarity index 98% rename from test/catalog.go rename to test/v1alpha1_samples/catalog.go index 501b74282..f8f700df3 100644 --- a/test/catalog.go +++ b/test/v1alpha1_samples/catalog.go @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package test +package testalpha import ( "context" @@ -21,7 +21,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/yaml" @@ -99,7 +99,7 @@ func (c *Catalog) BuildWithClusterBuildStrategyAndFalseSourceAnnotation(name str }, Spec: build.BuildSpec{ Source: build.Source{ - URL: pointer.String("foobar"), + URL: ptr.To[string]("foobar"), }, Strategy: build.Strategy{ Name: strategyName, @@ -122,7 +122,7 @@ func (c *Catalog) BuildWithClusterBuildStrategy(name string, ns string, strategy }, Spec: build.BuildSpec{ Source: build.Source{ - URL: pointer.String("https://github.com/shipwright-io/sample-go"), + URL: ptr.To[string]("https://github.com/shipwright-io/sample-go"), }, Strategy: build.Strategy{ Name: strategyName, @@ -148,7 +148,7 @@ func (c *Catalog) BuildWithClusterBuildStrategyAndSourceSecret(name string, ns s }, Spec: build.BuildSpec{ Source: build.Source{ - URL: pointer.String("https://github.com/shipwright-io/sample-go"), + URL: ptr.To[string]("https://github.com/shipwright-io/sample-go"), Credentials: &corev1.LocalObjectReference{ Name: "foobar", }, @@ -174,7 +174,7 @@ func (c *Catalog) BuildWithBuildStrategy(name string, ns string, strategyName st }, Spec: build.BuildSpec{ Source: build.Source{ - URL: pointer.String("https://github.com/shipwright-io/sample-go"), + URL: ptr.To[string]("https://github.com/shipwright-io/sample-go"), }, Strategy: build.Strategy{ Name: strategyName, @@ -193,7 +193,7 @@ func (c *Catalog) BuildWithNilBuildStrategyKind(name string, ns string, strategy }, Spec: build.BuildSpec{ Source: build.Source{ - URL: pointer.String("https://github.com/shipwright-io/sample-go"), + URL: ptr.To[string]("https://github.com/shipwright-io/sample-go"), }, Strategy: build.Strategy{ Name: strategyName, @@ -211,7 +211,7 @@ func (c *Catalog) BuildWithOutputSecret(name string, ns string, secretName strin }, Spec: build.BuildSpec{ Source: build.Source{ - URL: pointer.String("https://github.com/shipwright-io/sample-go"), + URL: ptr.To[string]("https://github.com/shipwright-io/sample-go"), }, Output: build.Image{ Credentials: &corev1.LocalObjectReference{ @@ -953,7 +953,7 @@ func (c *Catalog) BuildRunWithSA(buildRunName string, buildName string, saName s }, ServiceAccount: &build.ServiceAccount{ Name: &saName, - Generate: pointer.Bool(false), + Generate: ptr.To[bool](false), }, }, Status: build.BuildRunStatus{}, @@ -971,7 +971,7 @@ func (c *Catalog) BuildRunWithoutSA(buildRunName string, buildName string) *buil Name: buildName, }, ServiceAccount: &build.ServiceAccount{ - Generate: pointer.Bool(false), + Generate: ptr.To[bool](false), }, }, } @@ -989,7 +989,7 @@ func (c *Catalog) BuildRunWithSAGenerate(buildRunName string, buildName string) Name: buildName, }, ServiceAccount: &build.ServiceAccount{ - Generate: pointer.Bool(true), + Generate: ptr.To[bool](true), }, }, } diff --git a/test/clusterbuildstrategy_samples.go b/test/v1alpha1_samples/clusterbuildstrategy_samples.go similarity index 99% rename from test/clusterbuildstrategy_samples.go rename to test/v1alpha1_samples/clusterbuildstrategy_samples.go index 1dc2739d7..26072420b 100644 --- a/test/clusterbuildstrategy_samples.go +++ b/test/v1alpha1_samples/clusterbuildstrategy_samples.go @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package test +package testalpha // MinimalBuildahClusterBuildStrategy defines a // BuildStrategy for Buildah with two steps diff --git a/test/v1beta1_samples/build_samples.go b/test/v1beta1_samples/build_samples.go new file mode 100644 index 000000000..20427ec0c --- /dev/null +++ b/test/v1beta1_samples/build_samples.go @@ -0,0 +1,631 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package testbeta + +// MinimalBuildahBuildWithEnvVars defines a simple +// Build with a source, strategy, and env vars +const MinimalBuildahBuildWithEnvVars = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + name: buildah + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + env: + - name: MY_VAR_1 + value: "my-var-1-build-value" + - name: MY_VAR_2 + valueFrom: + fieldRef: + fieldPath: "my-fieldpath" +` + +// MinimalBuildahBuild defines a simple +// Build with a source and a strategy +const MinimalBuildahBuild = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + name: buildah + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile +` + +// BuildahBuildWithOutput defines a simple +// Build with a source, strategy and output +const BuildahBuildWithOutput = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah + namespace: build-test +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + name: buildah + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildahBuildWithAnnotationAndLabel defines a simple +// Build with a source, strategy, output, +// annotations and labels +const BuildahBuildWithAnnotationAndLabel = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + name: buildah + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + labels: + "maintainer": "team@my-company.com" + annotations: + "org.opencontainers.image.url": https://my-company.com/images +` + +// BuildahBuildWithMultipleAnnotationAndLabel defines a +// Build with a source, strategy, output, +// multiple annotations and labels +const BuildahBuildWithMultipleAnnotationAndLabel = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + name: buildah + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + labels: + "maintainer": "team@my-company.com" + "description": "This is my cool image" + annotations: + "org.opencontainers.image.url": https://my-company.com/images + "org.opencontainers.image.source": "https://github.com/org/repo" +` + +// BuildpacksBuildWithBuilderAndTimeOut defines a Build with +// source, strategy, builder, output and +// timeout +const BuildpacksBuildWithBuilderAndTimeOut = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildpacks-v3 + namespace: build-test +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + contextDir: docker-build + strategy: + name: buildpacks-v3 + kind: ClusterBuildStrategy + paramValues: + - name: dockerfile + value: Dockerfile + - name: builder-image + value: heroku/buildpacks:18 + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + timeout: 30s +` + +// BuildahBuildWithTimeOut defines a Build for +// Buildah with source, strategy, output and +// timeout +const BuildahBuildWithTimeOut = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildah + namespace: build-test +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + name: buildah + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + timeout: 30s +` + +// BuildBSMinimal defines a Build with a BuildStrategy +const BuildBSMinimal = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildBSMinimalNoSource defines a Build with a BuildStrategy without sources +const BuildBSMinimalNoSource = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: {} + strategy: + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildCBSMinimal defines a Build with a +// ClusterBuildStrategy +const BuildCBSMinimal = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + contextDir: docker-build + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildCBSMinimalWithFakeSecret defines a Build with a +// ClusterBuildStrategy and an not existing secret +const BuildCBSMinimalWithFakeSecret = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + pushSecret: fake-secret +` + +// BuildWithOutputRefSecret defines a Build with a +// referenced secret under spec.output +const BuildWithOutputRefSecret = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + pushSecret: output-secret + timeout: 5s +` + +// BuildWithSourceRefSecret defines a Build with a +// referenced secret under spec.source +const BuildWithSourceRefSecret = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + cloneSecret: source-secret + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + timeout: 5s +` + +// BuildWithBuilderRefSecret defines a Build with a +// referenced secret under spec.builder +const BuildWithBuilderRefSecret = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + paramValues: + - name: builder-image + value: heroku/buildpacks:18 + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + timeout: 5s +` + +// BuildWithMultipleRefSecrets defines a Build with +// multiple referenced secrets under spec +const BuildWithMultipleRefSecrets = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + cloneSecret: source-secret + paramValues: + - name: builder-image + value: heroku/buildpacks:18 + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + timeout: 5s +` + +// BuildCBSWithShortTimeOut defines a Build with a +// ClusterBuildStrategy and a short timeout +const BuildCBSWithShortTimeOut = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + timeout: 5s +` + +// BuildCBSWithShortTimeOutAndRefOutputSecret defines a Build with a +// ClusterBuildStrategy, a short timeout and an output secret +const BuildCBSWithShortTimeOutAndRefOutputSecret = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + pushSecret: foobarsecret + timeout: 5s +` + +// BuildCBSWithWrongURL defines a Build with a +// ClusterBuildStrategy and a non-existing url +const BuildCBSWithWrongURL = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + annotations: + build.shipwright.io/verify.repository: "true" +spec: + source: + git: + url: "https://github.foobar.com/sbose78/taxi" + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildCBSWithVerifyRepositoryAnnotation defines a Build +// with the verify repository annotation key +const BuildCBSWithVerifyRepositoryAnnotation = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + annotations: + build.shipwright.io/verify.repository: "" +spec: + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildCBSWithoutVerifyRepositoryAnnotation defines a minimal +// Build without source url and annotation +const BuildCBSWithoutVerifyRepositoryAnnotation = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildCBSWithBuildRunDeletion defines a Build with a +// ClusterBuildStrategy and the annotation for automatic BuildRun +// deletion +const BuildCBSWithBuildRunDeletion = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + strategy: + kind: ClusterBuildStrategy + retention: + atBuildDeletion: true + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithSleepTimeParam defines a Build with a parameter +const BuildWithSleepTimeParam = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + paramValues: + - name: sleep-time + value: "30" + strategy: + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithArrayParam defines a Build with an array parameter +const BuildWithArrayParam = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + paramValues: + - name: array-param + values: + - value: "3" + - value: "-1" + strategy: + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithConfigMapSecretParams defines a Build with parameter values referencing a ConfigMap and Secret +const BuildWithConfigMapSecretParams = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + paramValues: + - name: array-param + values: + - value: "3" + - configMapValue: + name: a-configmap + key: a-cm-key + - value: "-1" + - name: sleep-time + secretValue: + name: a-secret + key: a-secret-key + strategy: + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithRestrictedParam defines a Build using params that are reserved only +// for shipwright +const BuildWithRestrictedParam = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + paramValues: + - name: shp-something + value: "30" + - name: DOCKERFILE + value: "30" + strategy: + kind: BuildStrategy + 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 = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + paramValues: + - name: sleep-not + value: "30" + strategy: + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithEmptyStringParam defines a param that with an empty string value +const BuildWithEmptyStringParam = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + paramValues: + - name: sleep-time + value: "" + strategy: + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithUndefinedParamAndCBS defines a param that was not declared under the +// strategy parameters of a ClusterBuildStrategy +const BuildWithUndefinedParamAndCBS = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + paramValues: + - name: sleep-not + value: "30" + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// BuildWithSleepTimeParamAndCBS defines a Build with a parameter +const BuildWithSleepTimeParamAndCBS = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + paramValues: + - name: sleep-time + value: "30" + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// MinimalBuildWithRetentionTTLFive defines a simple +// Build with a source, a strategy and ttl +const MinimalBuildWithRetentionTTLFive = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: build-retention-ttl +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + contextDir: docker-build + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + retention: + ttlAfterFailed: 5s + ttlAfterSucceeded: 5s +` + +// MinimalBuildWithRetentionLimitOne defines a simple +// Build with a source, a strategy and limits set as 1 +const MinimalBuildWithRetentionLimitOne = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: build-retention-limit +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + contextDir: docker-build + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + retention: + failedLimit: 1 + succeededLimit: 1 +` + +// MinimalBuildWithRetentionLimitDiff defines a simple Build with a source, +// a strategy and different failed and succeeded limits +const MinimalBuildWithRetentionLimitDiff = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: build-retention-limit +spec: + source: + url: "https://github.com/shipwright-io/sample-go" + contextDir: docker-build + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + retention: + failedLimit: 1 + succeededLimit: 2 +` + +// MinimalBuildWithRetentionTTL defines a simple +// Build with a source, a strategy ttl +const MinimalBuildWithRetentionTTLOneMin = ` +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: build-retention-ttl +spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + contextDir: docker-build + strategy: + kind: ClusterBuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app + retention: + ttlAfterFailed: 1m + ttlAfterSucceeded: 1m +` diff --git a/test/v1beta1_samples/buildrun_samples.go b/test/v1beta1_samples/buildrun_samples.go new file mode 100644 index 000000000..0d8676400 --- /dev/null +++ b/test/v1beta1_samples/buildrun_samples.go @@ -0,0 +1,253 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package testbeta + +// MinimalBuildahBuildRunWithEnvVars defines a simple +// BuildRun with a referenced Build and env vars +const MinimalBuildahBuildRunWithEnvVars = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-run +spec: + build: + name: buildah + env: + - name: MY_VAR_2 + value: "my-var-2-buildrun-value" + - name: MY_VAR_3 + valueFrom: + fieldRef: + fieldPath: "my-fieldpath" +` + +// BuildahBuildRunWithOutputImageLabelsAndAnnotations defines a BuildRun +// with a output image labels and annotation +const BuildahBuildRunWithOutputImageLabelsAndAnnotations = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-run + namespace: build-test +spec: + build: + name: buildah + serviceAccount: buildpacks-v3-serviceaccount + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app-v2 + labels: + "maintainer": "new-team@my-company.com" + "foo": "bar" + annotations: + "org.opencontainers.owner": "my-company" +` + +// MinimalBuildahBuildRun defines a simple +// BuildRun with a referenced Build +const MinimalBuildahBuildRun = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-run +spec: + build: + name: buildah +` + +// BuildahBuildRunWithSA defines a BuildRun +// with a service-account +const BuildahBuildRunWithSA = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-run + namespace: build-test +spec: + build: + name: buildah + serviceAccount: buildpacks-v3-serviceaccount +` + +// BuildahBuildRunWithSAAndOutput defines a BuildRun +// with a service-account and output overrides +const BuildahBuildRunWithSAAndOutput = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-run + namespace: build-test +spec: + build: + name: buildah + serviceAccount: buildpacks-v3-serviceaccount + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app-v2 +` + +// BuildpacksBuildRunWithSA defines a BuildRun +// with a service-account +const BuildpacksBuildRunWithSA = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildpacks-v3-run + namespace: build-test +spec: + build: + name: buildpacks-v3 + serviceAccount: buildpacks-v3-serviceaccount +` + +// BuildahBuildRunWithTimeOutAndSA defines a BuildRun +// with a service-account and timeout +const BuildahBuildRunWithTimeOutAndSA = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-run + namespace: build-test +spec: + build: + name: buildah + serviceAccount: buildpacks-v3-serviceaccount + timeout: 1m +` + +// MinimalBuildRun defines a minimal BuildRun +// with a reference to a not existing Build +const MinimalBuildRun = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + build: + name: foobar +` + +const MinimalOneOffBuildRun = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: standalone-buildrun +spec: + build: + spec: + source: + git: + url: "https://github.com/shipwright-io/sample-go" + contextDir: docker-build + strategy: + kind: ClusterBuildStrategy + name: buildah + output: + image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app +` + +// MinimalBuildRunWithParams defines a param override +const MinimalBuildRunWithParams = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + paramValues: + - name: sleep-time + value: "15" + build: + name: foobar +` + +const MinimalBuildRunWithReservedParams = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + paramValues: + - name: shp-sleep-time + value: "15" + build: + name: foobar +` + +// MinimalBuildRunWithSpecifiedServiceAccount defines a minimal BuildRun +// with a reference to a not existing serviceAccount +const MinimalBuildRunWithSpecifiedServiceAccount = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + build: + name: buildah + serviceAccount: foobar +` + +// MinimalBuildRunWithSAGeneration defines a minimal BuildRun +// with a reference to a not existing Build +const MinimalBuildRunWithSAGeneration = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + serviceAccount: ".generate" + build: + name: foobar +` + +// MinimalBuildRunWithTimeOut defines a BuildRun with +// an override for the Build Timeout +const MinimalBuildRunWithTimeOut = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + timeout: 1s + build: + name: foobar +` + +// MinimalBuildRunWithOutput defines a BuildRun with +// an override for the Build Output +const MinimalBuildRunWithOutput = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +spec: + output: + image: foobar.registry.com + build: + name: foobar +` + +// MinimalBuildRunRetention defines a minimal BuildRun +// with a reference used to test retention fields +const MinimalBuildRunRetention = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buidrun-retention-ttl +spec: + build: + name: build-retention-ttl +` + +// MinimalBuildRunRetention defines a minimal BuildRun +// with a reference used to test retention fields +const MinimalBuildRunRetentionTTLFive = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buidrun-retention-ttl +spec: + build: + name: build-retention-ttl + retention: + ttlAfterFailed: 5s + ttlAfterSucceeded: 5s +` + +const MinimalBuildahBuildRunWithExitCode = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: buildah-run +spec: + paramValues: + - name: exit-command + value: "true" + build: + name: buildah +` diff --git a/test/v1beta1_samples/buildstrategy_samples.go b/test/v1beta1_samples/buildstrategy_samples.go new file mode 100644 index 000000000..fcf19c81f --- /dev/null +++ b/test/v1beta1_samples/buildstrategy_samples.go @@ -0,0 +1,369 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package testbeta + +// MinimalBuildahBuildStrategy defines a +// BuildStrategy for Buildah with two steps +// each of them with different container resources +const MinimalBuildahBuildStrategy = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: buildah +spec: + volumes: + - name: buildah-images + emptyDir: {} + steps: + - name: buildah-bud + image: quay.io/containers/buildah:v1.31.0 + workingDir: $(params.shp-source-root) + securityContext: + privileged: true + command: + - /usr/bin/buildah + args: + - bud + - --tag=$(params.shp-output-image) + - --file=$(build.dockerfile) + - $(params.shp-source-context) + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 500m + memory: 1Gi + volumeMounts: + - name: buildah-images + mountPath: /var/lib/containers/storage + - name: buildah-push + image: quay.io/containers/buildah:v1.31.0 + securityContext: + privileged: true + command: + - /usr/bin/buildah + args: + - push + - --tls-verify=false + - docker://$(params.shp-output-image) + resources: + limits: + cpu: 100m + memory: 65Mi + requests: + cpu: 100m + memory: 65Mi + volumeMounts: + - name: buildah-images + mountPath: /var/lib/containers/storage +` + +// MinimalBuildahBuildStrategyWithEnvs defines a +// BuildStrategy for Buildah with two steps +// each of them with different container resources +// and env vars +const MinimalBuildahBuildStrategyWithEnvs = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: buildah +spec: + volumes: + - name: buildah-images + emptyDir: {} + steps: + - name: buildah-bud + image: quay.io/containers/buildah:v1.31.0 + workingDir: $(params.shp-source-root) + securityContext: + privileged: true + command: + - /usr/bin/buildah + args: + - bud + - --tag=$(params.shp-output-image) + - --file=$(build.dockerfile) + - $(params.shp-source-context) + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 500m + memory: 1Gi + volumeMounts: + - name: buildah-images + mountPath: /var/lib/containers/storage + env: + - name: MY_VAR_1 + value: "my-var-1-buildstrategy-value" + - name: MY_VAR_2 + valueFrom: + fieldRef: + fieldPath: "my-fieldpath" + - name: buildah-push + image: quay.io/containers/buildah:v1.31.0 + securityContext: + privileged: true + command: + - /usr/bin/buildah + args: + - push + - --tls-verify=false + - docker://$(params.shp-output-image) + resources: + limits: + cpu: 100m + memory: 65Mi + requests: + cpu: 100m + memory: 65Mi + volumeMounts: + - name: buildah-images + mountPath: /var/lib/containers/storage +` + +// BuildahBuildStrategySingleStep defines a +// BuildStrategy for Buildah with a single step +// and container resources +const BuildahBuildStrategySingleStep = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + annotations: + kubernetes.io/ingress-bandwidth: 1M + clusterbuildstrategy.shipwright.io/dummy: aValue + kubectl.kubernetes.io/last-applied-configuration: anotherValue + kubernetes.io/egress-bandwidth: 1M + name: buildah +spec: + volumes: + - name: varlibcontainers + emptyDir: {} + steps: + - name: build + image: "$(build.builder.image)" + workingDir: $(params.shp-source-root) + command: + - buildah + - bud + - --tls-verify=false + - --layers + - -f + - $(build.dockerfile) + - -t + - $(params.shp-output-image) + - $(params.shp-source-context) + resources: + limits: + cpu: 500m + memory: 2Gi + requests: + cpu: 500m + memory: 2Gi + volumeMounts: + - name: varlibcontainers + mountPath: /var/lib/containers +` + +// BuildpacksBuildStrategySingleStep defines a +// BuildStrategy for Buildpacks with a single step +// and container resources +const BuildpacksBuildStrategySingleStep = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: buildpacks-v3 +spec: + volumes: + - name: varlibcontainers + emptyDir: {} + steps: + - name: build + image: "$(build.builder.image)" + workingDir: $(params.shp-source-root) + command: + - /cnb/lifecycle/builder + - -app + - $(params.shp-source-context) + - -layers + - /layers + - -group + - /layers/group.toml + - plan + - /layers/plan.toml + resources: + limits: + cpu: 500m + memory: 2Gi + requests: + cpu: 500m + memory: 2Gi + volumeMounts: + - name: varlibcontainers + mountPath: /var/lib/containers +` + +// BuildStrategyWithParameters is a strategy that uses a +// sleep command with a value for its spec.parameters +const BuildStrategyWithParameters = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: strategy-with-param +spec: + parameters: + - name: sleep-time + description: "time in seconds for sleeping" + default: "1" + - name: array-param + description: "An arbitrary array" + type: array + defaults: [] + steps: + - name: sleep30 + image: alpine:latest + command: + - sleep + args: + - $(params.sleep-time) + - name: echo-array-sum + image: alpine:latest + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + sum=0 + + for var in "$@" + do + sum=$((sum+var)) + done + + echo "Sum: ${sum}" + - -- + - $(params.array-param[*]) +` + +// BuildStrategyWithoutDefaultInParameter is a strategy that uses a +// sleep command with a value from its spec.parameters, where the parameter +// have no default +const BuildStrategyWithoutDefaultInParameter = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: strategy-with-param-and-no-default +spec: + parameters: + - name: sleep-time + description: "time in seconds for sleeping" + steps: + - name: sleep30 + image: alpine:latest + command: + - sleep + args: + - $(params.sleep-time) +` + +// BuildStrategyWithErrorResult is a strategy that always fails +// and surfaces and error reason and message to the user +const BuildStrategyWithErrorResult = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: strategy-with-error-results +spec: + steps: + - name: fail-with-error-result + image: alpine:latest + command: + - sh + args: + - -c + - | + printf "integration test error reason" > $(results.shp-error-reason.path); + printf "integration test error message" > $(results.shp-error-message.path); + return 1 +` + +// BuildStrategyWithParameterVerification is a strategy that verifies that parameters can be used at all expected places +const BuildStrategyWithParameterVerification = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: strategy-with-parameter-verification +spec: + parameters: + - name: env1 + description: "This parameter will be used in the env of the build step" + type: string + - name: env2 + description: "This parameter will be used in the env of the build step" + type: string + - name: env3 + description: "This parameter will be used in the env of the build step" + type: string + - name: image + description: "This parameter will be used as the image of the build step" + type: string + - name: commands + description: "This parameter will be used as the command of the build step" + type: array + - name: args + description: "This parameter will be used as the args of the build step" + type: array + steps: + - name: calculate-sum + image: $(params.image) + env: + - name: ENV_FROM_PARAMETER1 + value: $(params.env1) + - name: ENV_FROM_PARAMETER2 + value: $(params.env2) + - name: ENV_FROM_PARAMETER3 + value: $(params.env3) + command: + - $(params.commands[*]) + args: + - | + set -euo pipefail + + sum=$((ENV_FROM_PARAMETER1 + ENV_FROM_PARAMETER2 + ENV_FROM_PARAMETER3)) + + for var in "$@" + do + sum=$((sum+var)) + done + + echo "Sum: ${sum}" + # Once we have strategy-defined results, then those would be better suitable + # Until then, just store it as image size :-) + echo -n "${sum}" > '$(results.shp-image-size.path)' + - -- + - $(params.args[*]) +` + +// BuildStrategyWithoutPush is a strategy that writes an image tarball and pushes nothing +const BuildStrategyWithoutPush = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: strategy-without-push +spec: + steps: + - name: store-tarball + image: gcr.io/go-containerregistry/crane:v0.16.1 + command: + - crane + args: + - export + - busybox + - $(params.shp-output-directory)/image.tar +` diff --git a/test/v1beta1_samples/catalog.go b/test/v1beta1_samples/catalog.go new file mode 100644 index 000000000..675ec595f --- /dev/null +++ b/test/v1beta1_samples/catalog.go @@ -0,0 +1,1092 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package testbeta + +import ( + "context" + "fmt" + "strconv" + "time" + + . "github.com/onsi/gomega" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "knative.dev/pkg/apis" + knativev1 "knative.dev/pkg/apis/duck/v1" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/yaml" + + build "github.com/shipwright-io/build/pkg/apis/build/v1beta1" +) + +// Catalog allows you to access helper functions +type Catalog struct{} + +// SecretWithAnnotation gives you a secret with build annotation +func (c *Catalog) SecretWithAnnotation(name string, ns string) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + Annotations: map[string]string{build.AnnotationBuildRefSecret: "true"}, + }, + } +} + +// SecretWithoutAnnotation gives you a secret without build annotation +func (c *Catalog) SecretWithoutAnnotation(name string, ns string) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } +} + +// SecretWithStringData creates a Secret with stringData (not base64 encoded) +func (c *Catalog) SecretWithStringData(name string, ns string, stringData map[string]string) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + StringData: stringData, + } +} + +// SecretWithDockerConfigJson creates a secret of type dockerconfigjson +func (c *Catalog) SecretWithDockerConfigJson(name string, ns string, host string, username string, password string) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Type: corev1.SecretTypeDockerConfigJson, + StringData: map[string]string{ + ".dockerconfigjson": fmt.Sprintf("{\"auths\":{%q:{\"username\":%q,\"password\":%q}}}", host, username, password), + }, + } +} + +// ConfigMapWithData creates a ConfigMap with data +func (c *Catalog) ConfigMapWithData(name string, ns string, data map[string]string) *corev1.ConfigMap { + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Data: data, + } +} + +// BuildWithClusterBuildStrategyAndFalseSourceAnnotation gives you an specific Build CRD +func (c *Catalog) BuildWithClusterBuildStrategyAndFalseSourceAnnotation(name string, ns string, strategyName string) *build.Build { + buildStrategy := build.ClusterBuildStrategyKind + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + Annotations: map[string]string{build.AnnotationBuildVerifyRepository: "false"}, + }, + Spec: build.BuildSpec{ + Source: build.Source{ + GitSource: &build.Git{ + URL: ptr.To[string]("foobar"), + }, + }, + Strategy: build.Strategy{ + Name: strategyName, + Kind: &buildStrategy, + }, + Output: build.Image{ + Image: "foobar", + }, + }, + } +} + +// BuildWithClusterBuildStrategy gives you an specific Build CRD +func (c *Catalog) BuildWithClusterBuildStrategy(name string, ns string, strategyName string, secretName string) *build.Build { + buildStrategy := build.ClusterBuildStrategyKind + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: build.BuildSpec{ + Source: build.Source{ + GitSource: &build.Git{ + URL: ptr.To[string]("https://github.com/shipwright-io/sample-go"), + }, + }, + Strategy: build.Strategy{ + Name: strategyName, + Kind: &buildStrategy, + }, + Output: build.Image{ + Image: "foobar", + PushSecret: &secretName, + }, + }, + } +} + +// BuildWithClusterBuildStrategyAndSourceSecret gives you an specific Build CRD +func (c *Catalog) BuildWithClusterBuildStrategyAndSourceSecret(name string, ns string, strategyName string) *build.Build { + buildStrategy := build.ClusterBuildStrategyKind + secret := "foobar" + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: build.BuildSpec{ + Source: build.Source{ + GitSource: &build.Git{ + URL: ptr.To[string]("https://github.com/shipwright-io/sample-go"), + CloneSecret: &secret, + }, + }, + Strategy: build.Strategy{ + Name: strategyName, + Kind: &buildStrategy, + }, + Output: build.Image{ + Image: "foobar", + }, + }, + } +} + +// BuildWithBuildStrategy gives you an specific Build CRD +func (c *Catalog) BuildWithBuildStrategy(name string, ns string, strategyName string) *build.Build { + buildStrategy := build.NamespacedBuildStrategyKind + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: build.BuildSpec{ + Source: build.Source{ + GitSource: &build.Git{ + URL: ptr.To[string]("https://github.com/shipwright-io/sample-go"), + }, + }, + Strategy: build.Strategy{ + Name: strategyName, + Kind: &buildStrategy, + }, + }, + } +} + +// BuildWithNilBuildStrategyKind gives you an Build CRD with nil build strategy kind +func (c *Catalog) BuildWithNilBuildStrategyKind(name string, ns string, strategyName string) *build.Build { + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: build.BuildSpec{ + Source: build.Source{ + GitSource: &build.Git{ + URL: ptr.To[string]("https://github.com/shipwright-io/sample-go"), + }, + }, + Strategy: build.Strategy{ + Name: strategyName, + }, + }, + } +} + +// BuildWithOutputSecret .... +func (c *Catalog) BuildWithOutputSecret(name string, ns string, secretName string) *build.Build { + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: build.BuildSpec{ + Source: build.Source{ + GitSource: &build.Git{ + URL: ptr.To[string]("https://github.com/shipwright-io/sample-go"), + }, + }, + Output: build.Image{ + PushSecret: &secretName, + }, + }, + } +} + +// ClusterBuildStrategy to support tests +func (c *Catalog) ClusterBuildStrategy(name string) *build.ClusterBuildStrategy { + return &build.ClusterBuildStrategy{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } +} + +// FakeClusterBuildStrategyNotFound returns a not found error +func (c *Catalog) FakeClusterBuildStrategyNotFound(name string) error { + return errors.NewNotFound(schema.GroupResource{}, name) +} + +// StubFunc is used to simulate the status of the Build +// after a .Status().Update() call in the controller. This +// receives a parameter to return an specific status state +func (c *Catalog) StubFunc(status corev1.ConditionStatus, reason build.BuildReason, message string) func(context context.Context, object client.Object, _ ...client.UpdateOption) error { + return func(context context.Context, object client.Object, _ ...client.UpdateOption) error { + switch object := object.(type) { + case *build.Build: + Expect(*object.Status.Registered).To(Equal(status)) + Expect(*object.Status.Reason).To(Equal(reason)) + Expect(*object.Status.Message).To(Equal(message)) + } + return nil + } +} + +// StubBuildUpdateOwnerReferences simulates and assert an updated +// BuildRun object ownerreferences +func (c *Catalog) StubBuildUpdateOwnerReferences(ownerKind string, ownerName string, isOwnerController *bool, blockOwnerDeletion *bool) func(context context.Context, object client.Object, _ ...client.UpdateOption) error { + return func(context context.Context, object client.Object, _ ...client.UpdateOption) error { + switch object := object.(type) { + case *build.BuildRun: + Expect(object.OwnerReferences[0].Kind).To(Equal(ownerKind)) + Expect(object.OwnerReferences[0].Name).To(Equal(ownerName)) + Expect(object.OwnerReferences[0].Controller).To(Equal(isOwnerController)) + Expect(object.OwnerReferences[0].BlockOwnerDeletion).To(Equal(blockOwnerDeletion)) + Expect(len(object.OwnerReferences)).ToNot(Equal(0)) + } + return nil + } +} + +// StubBuildRun is used to simulate the existence of a BuildRun +// only when there is a client GET on this object type +func (c *Catalog) StubBuildRun( + b *build.BuildRun, +) func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + return func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + switch object := object.(type) { + case *build.BuildRun: + b.DeepCopyInto(object) + return nil + } + return errors.NewNotFound(schema.GroupResource{}, nn.Name) + } +} + +// StubBuildRunAndTaskRun is used to simulate the existence of a BuildRun +// and a TaskRun when there is a client GET on this two objects +func (c *Catalog) StubBuildRunAndTaskRun( + b *build.BuildRun, + tr *v1beta1.TaskRun, +) func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + return func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + switch object := object.(type) { + case *build.BuildRun: + b.DeepCopyInto(object) + return nil + case *v1beta1.TaskRun: + tr.DeepCopyInto(object) + return nil + } + return errors.NewNotFound(schema.GroupResource{}, nn.Name) + } +} + +// StubBuildAndTaskRun is used to simulate the existence of a Build +// and a TaskRun when there is a client GET on this two objects +func (c *Catalog) StubBuildAndTaskRun( + b *build.Build, + tr *v1beta1.TaskRun, +) func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + return func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + switch object := object.(type) { + case *build.Build: + b.DeepCopyInto(object) + return nil + case *v1beta1.TaskRun: + tr.DeepCopyInto(object) + return nil + } + return errors.NewNotFound(schema.GroupResource{}, nn.Name) + } +} + +// StubBuildStatusReason asserts Status fields on a Build resource +func (c *Catalog) StubBuildStatusReason(reason build.BuildReason, message string) func(context context.Context, object client.Object, _ ...client.UpdateOption) error { + return func(context context.Context, object client.Object, _ ...client.UpdateOption) error { + switch object := object.(type) { + case *build.Build: + if object.Status.Message != nil && *object.Status.Message != "" { + Expect(*object.Status.Message).To(Equal(message)) + } + if object.Status.Reason != nil && *object.Status.Reason != "" { + Expect(*object.Status.Reason).To(Equal(reason)) + } + } + return nil + } +} + +// StubBuildRunStatus asserts Status fields on a BuildRun resource +func (c *Catalog) StubBuildRunStatus(reason string, name *string, condition build.Condition, status corev1.ConditionStatus, buildSpec build.BuildSpec, tolerateEmptyStatus bool) func(context context.Context, object client.Object, _ ...client.UpdateOption) error { + return func(context context.Context, object client.Object, _ ...client.UpdateOption) error { + switch object := object.(type) { + case *build.BuildRun: + if !tolerateEmptyStatus { + Expect(object.Status.GetCondition(build.Succeeded).Status).To(Equal(condition.Status)) + Expect(object.Status.GetCondition(build.Succeeded).Reason).To(Equal(condition.Reason)) + Expect(object.Status.TaskRunName).To(Equal(name)) + } + if object.Status.BuildSpec != nil { + Expect(*object.Status.BuildSpec).To(Equal(buildSpec)) + } + } + return nil + } +} + +// StubBuildRunLabel asserts Label fields on a BuildRun resource +func (c *Catalog) StubBuildRunLabel(buildSample *build.Build) func(context context.Context, object client.Object, _ ...client.UpdateOption) error { + return func(context context.Context, object client.Object, _ ...client.UpdateOption) error { + switch object := object.(type) { + case *build.BuildRun: + Expect(object.Labels[build.LabelBuild]).To(Equal(buildSample.Name)) + Expect(object.Labels[build.LabelBuildGeneration]).To(Equal(strconv.FormatInt(buildSample.Generation, 10))) + } + return nil + } +} + +// StubBuildRunGetWithoutSA simulates the output of client GET calls +// for the BuildRun unit tests +func (c *Catalog) StubBuildRunGetWithoutSA( + b *build.Build, + br *build.BuildRun, +) func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + return func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + switch object := object.(type) { + case *build.Build: + b.DeepCopyInto(object) + return nil + case *build.BuildRun: + br.DeepCopyInto(object) + return nil + } + return errors.NewNotFound(schema.GroupResource{}, nn.Name) + } +} + +// StubBuildRunGetWithTaskRunAndSA returns fake object for different +// client calls +func (c *Catalog) StubBuildRunGetWithTaskRunAndSA( + b *build.Build, + br *build.BuildRun, + tr *v1beta1.TaskRun, + sa *corev1.ServiceAccount, +) func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + return func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + switch object := object.(type) { + case *build.Build: + b.DeepCopyInto(object) + return nil + case *build.BuildRun: + br.DeepCopyInto(object) + return nil + case *v1beta1.TaskRun: + tr.DeepCopyInto(object) + return nil + case *corev1.ServiceAccount: + sa.DeepCopyInto(object) + return nil + } + return errors.NewNotFound(schema.GroupResource{}, nn.Name) + } +} + +// StubBuildRunGetWithSA returns fake object for different +// client calls +func (c *Catalog) StubBuildRunGetWithSA( + b *build.Build, + br *build.BuildRun, + sa *corev1.ServiceAccount, +) func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + return func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + switch object := object.(type) { + case *build.Build: + b.DeepCopyInto(object) + return nil + case *build.BuildRun: + br.DeepCopyInto(object) + return nil + case *corev1.ServiceAccount: + sa.DeepCopyInto(object) + return nil + } + return errors.NewNotFound(schema.GroupResource{}, nn.Name) + } +} + +// StubBuildRunGetWithSAandStrategies simulates the output of client GET +// calls for the BuildRun unit tests +func (c *Catalog) StubBuildRunGetWithSAandStrategies( + b *build.Build, + br *build.BuildRun, + sa *corev1.ServiceAccount, + cb *build.ClusterBuildStrategy, + bs *build.BuildStrategy, +) func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + return func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + switch object := object.(type) { + case *build.Build: + if b != nil { + b.DeepCopyInto(object) + return nil + } + case *build.BuildRun: + if br != nil { + br.DeepCopyInto(object) + return nil + } + case *corev1.ServiceAccount: + if sa != nil { + sa.DeepCopyInto(object) + return nil + } + case *build.ClusterBuildStrategy: + if cb != nil { + cb.DeepCopyInto(object) + return nil + } + case *build.BuildStrategy: + if bs != nil { + bs.DeepCopyInto(object) + return nil + } + } + return errors.NewNotFound(schema.GroupResource{}, nn.Name) + } +} + +func (c *Catalog) StubBuildCRDs( + b *build.Build, + br *build.BuildRun, + sa *corev1.ServiceAccount, + cb *build.ClusterBuildStrategy, + bs *build.BuildStrategy, +) func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + return func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + switch object := object.(type) { + case *build.Build: + b.DeepCopyInto(object) + return nil + case *build.BuildRun: + br.DeepCopyInto(object) + return nil + case *corev1.ServiceAccount: + sa.DeepCopyInto(object) + return nil + case *build.ClusterBuildStrategy: + cb.DeepCopyInto(object) + return nil + case *build.BuildStrategy: + bs.DeepCopyInto(object) + return nil + } + return errors.NewNotFound(schema.GroupResource{}, nn.Name) + } +} + +// StubBuildCRDsPodAndTaskRun stubs different objects in case a client +// GET call is executed against them +func (c *Catalog) StubBuildCRDsPodAndTaskRun( + b *build.Build, + br *build.BuildRun, + sa *corev1.ServiceAccount, + cb *build.ClusterBuildStrategy, + bs *build.BuildStrategy, + tr *v1beta1.TaskRun, + pod *corev1.Pod, +) func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + return func(context context.Context, nn types.NamespacedName, object client.Object, getOptions ...client.GetOption) error { + switch object := object.(type) { + case *build.Build: + b.DeepCopyInto(object) + return nil + case *build.BuildRun: + br.DeepCopyInto(object) + return nil + case *corev1.ServiceAccount: + sa.DeepCopyInto(object) + return nil + case *build.ClusterBuildStrategy: + cb.DeepCopyInto(object) + return nil + case *build.BuildStrategy: + bs.DeepCopyInto(object) + return nil + case *v1beta1.TaskRun: + tr.DeepCopyInto(object) + return nil + case *corev1.Pod: + pod.DeepCopyInto(object) + return nil + } + return errors.NewNotFound(schema.GroupResource{}, nn.Name) + } +} + +// TaskRunWithStatus returns a minimal tekton TaskRun with an Status +func (c *Catalog) TaskRunWithStatus(trName string, ns string) *v1beta1.TaskRun { + return &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: trName, + Namespace: ns, + }, + Spec: v1beta1.TaskRunSpec{ + Timeout: &metav1.Duration{ + Duration: time.Minute * 2, + }, + }, + Status: v1beta1.TaskRunStatus{ + Status: knativev1.Status{ + Conditions: knativev1.Conditions{ + { + Type: apis.ConditionSucceeded, + Reason: "Unknown", + Status: corev1.ConditionUnknown, + }, + }, + }, + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + PodName: "foobar-pod", + StartTime: &metav1.Time{ + Time: time.Now(), + }, + CompletionTime: &metav1.Time{ + Time: time.Now(), + }, + }, + }, + } +} + +// DefaultTaskRunWithStatus returns a minimal tekton TaskRun with an Status +func (c *Catalog) DefaultTaskRunWithStatus(trName string, buildRunName string, ns string, status corev1.ConditionStatus, reason string) *v1beta1.TaskRun { + return &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: trName, + Namespace: ns, + Labels: map[string]string{"buildrun.shipwright.io/name": buildRunName}, + }, + Spec: v1beta1.TaskRunSpec{}, + Status: v1beta1.TaskRunStatus{ + Status: knativev1.Status{ + Conditions: knativev1.Conditions{ + { + Type: apis.ConditionSucceeded, + Reason: reason, + Status: status, + }, + }, + }, + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + StartTime: &metav1.Time{ + Time: time.Now(), + }, + }, + }, + } +} + +// TaskRunWithCompletionAndStartTime provides a TaskRun object with a +// Completion and StartTime +func (c *Catalog) TaskRunWithCompletionAndStartTime(trName string, buildRunName string, ns string) *v1beta1.TaskRun { + return &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: trName, + Namespace: ns, + Labels: map[string]string{"buildrun.shipwright.io/name": buildRunName}, + }, + Spec: v1beta1.TaskRunSpec{}, + Status: v1beta1.TaskRunStatus{ + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + CompletionTime: &metav1.Time{ + Time: time.Now(), + }, + StartTime: &metav1.Time{ + Time: time.Now(), + }, + PodName: "foobar", + }, + Status: knativev1.Status{ + Conditions: knativev1.Conditions{ + { + Type: apis.ConditionSucceeded, + Reason: "something bad happened", + Status: corev1.ConditionFalse, + Message: "some message", + }, + }, + }, + }, + } +} + +// DefaultTaskRunWithFalseStatus returns a minimal tektont TaskRun with a FALSE status +func (c *Catalog) DefaultTaskRunWithFalseStatus(trName string, buildRunName string, ns string) *v1beta1.TaskRun { + return &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: trName, + Namespace: ns, + Labels: map[string]string{"buildrun.shipwright.io/name": buildRunName}, + }, + Spec: v1beta1.TaskRunSpec{}, + Status: v1beta1.TaskRunStatus{ + Status: knativev1.Status{ + Conditions: knativev1.Conditions{ + { + Type: apis.ConditionSucceeded, + Reason: "something bad happened", + Status: corev1.ConditionFalse, + Message: "some message", + }, + }, + }, + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + StartTime: &metav1.Time{ + Time: time.Now(), + }, + }, + }, + } +} + +// DefaultBuild returns a minimal Build object +func (c *Catalog) DefaultBuild(buildName string, strategyName string, strategyKind build.BuildStrategyKind) *build.Build { + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildName, + }, + Spec: build.BuildSpec{ + Strategy: build.Strategy{ + Name: strategyName, + Kind: &strategyKind, + }, + }, + Status: build.BuildStatus{ + Registered: build.ConditionStatusPtr(corev1.ConditionTrue), + }, + } +} + +// BuildWithoutStrategyKind returns a minimal Build object without an strategy kind definition +func (c *Catalog) BuildWithoutStrategyKind(buildName string, strategyName string) *build.Build { + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildName, + }, + Spec: build.BuildSpec{ + Strategy: build.Strategy{ + Name: strategyName, + }, + }, + Status: build.BuildStatus{ + Registered: build.ConditionStatusPtr(corev1.ConditionTrue), + }, + } +} + +// BuildWithBuildRunDeletions returns a minimal Build object with the +// build.shipwright.io/build-run-deletion annotation set to true +func (c *Catalog) BuildWithBuildRunDeletions(buildName string, strategyName string, strategyKind build.BuildStrategyKind) *build.Build { + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildName, + }, + Spec: build.BuildSpec{ + Strategy: build.Strategy{ + Name: strategyName, + Kind: &strategyKind, + }, + Retention: &build.BuildRetention{ + AtBuildDeletion: ptr.To[bool](true), + }, + }, + Status: build.BuildStatus{ + Registered: build.ConditionStatusPtr(corev1.ConditionTrue), + }, + } +} + +// BuildWithBuildRunDeletionsAndFakeNS returns a minimal Build object with the +// build.shipwright.io/build-run-deletion annotation set to true in a fake namespace +func (c *Catalog) BuildWithBuildRunDeletionsAndFakeNS(buildName string, strategyName string, strategyKind build.BuildStrategyKind) *build.Build { + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildName, + Namespace: "fakens", + }, + Spec: build.BuildSpec{ + Strategy: build.Strategy{ + Name: strategyName, + Kind: &strategyKind, + }, + Retention: &build.BuildRetention{ + AtBuildDeletion: ptr.To[bool](true), + }, + }, + Status: build.BuildStatus{ + Registered: build.ConditionStatusPtr(corev1.ConditionTrue), + }, + } +} + +// DefaultBuildWithFalseRegistered returns a minimal Build object with a FALSE Registered +func (c *Catalog) DefaultBuildWithFalseRegistered(buildName string, strategyName string, strategyKind build.BuildStrategyKind) *build.Build { + return &build.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildName, + }, + Spec: build.BuildSpec{ + Strategy: build.Strategy{ + Name: strategyName, + Kind: &strategyKind, + }, + }, + Status: build.BuildStatus{ + Registered: build.ConditionStatusPtr(corev1.ConditionFalse), + Reason: build.BuildReasonPtr("something bad happened"), + }, + } +} + +// DefaultBuildRun returns a minimal BuildRun object +func (c *Catalog) DefaultBuildRun(buildRunName string, buildName string) *build.BuildRun { + var defaultBuild = c.DefaultBuild(buildName, "foobar-strategy", build.ClusterBuildStrategyKind) + return &build.BuildRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildRunName, + }, + Spec: build.BuildRunSpec{ + Build: &build.ReferencedBuild{ + Name: buildName, + }, + }, + Status: build.BuildRunStatus{ + BuildSpec: &defaultBuild.Spec, + }, + } +} + +// PodWithInitContainerStatus returns a pod with a single +// entry under the Status field for InitContainer Status +func (c *Catalog) PodWithInitContainerStatus(podName string, initContainerName string) *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + }, + Status: corev1.PodStatus{ + InitContainerStatuses: []corev1.ContainerStatus{ + { + Name: initContainerName, + }, + }, + }, + } +} + +// BuildRunWithBuildSnapshot returns BuildRun Object with a populated +// BuildSpec in the Status field +func (c *Catalog) BuildRunWithBuildSnapshot(buildRunName string, buildName string) *build.BuildRun { + return &build.BuildRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildRunName, + CreationTimestamp: metav1.Time{ + Time: time.Now(), + }, + }, + Status: build.BuildRunStatus{ + BuildSpec: &build.BuildSpec{ + Strategy: build.Strategy{ + Name: "foobar", + }, + }, + }, + Spec: build.BuildRunSpec{ + Build: &build.ReferencedBuild{ + Name: buildName, + }, + }, + } +} + +// BuildRunWithExistingOwnerReferences returns a BuildRun object that is +// already owned by some fake object +func (c *Catalog) BuildRunWithExistingOwnerReferences(buildRunName string, buildName string, ownerName string) *build.BuildRun { + + managingController := true + + fakeOwnerRef := metav1.OwnerReference{ + APIVersion: ownerName, + Kind: ownerName, + Controller: &managingController, + } + + return &build.BuildRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildRunName, + OwnerReferences: []metav1.OwnerReference{fakeOwnerRef}, + }, + Spec: build.BuildRunSpec{ + Build: &build.ReferencedBuild{ + Name: buildName, + }, + }, + } +} + +// BuildRunWithFakeNamespace returns a BuildRun object with +// a namespace that does not exist +func (c *Catalog) BuildRunWithFakeNamespace(buildRunName string, buildName string) *build.BuildRun { + return &build.BuildRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildRunName, + Namespace: "foobarns", + }, + Spec: build.BuildRunSpec{ + Build: &build.ReferencedBuild{ + Name: buildName, + }, + }, + } +} + +// DefaultTaskRun returns a minimal TaskRun object +func (c *Catalog) DefaultTaskRun(taskRunName string, ns string) *v1beta1.TaskRun { + return &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: taskRunName, + Namespace: ns, + }, + } +} + +// DefaultServiceAccount returns a minimal SA object +func (c *Catalog) DefaultServiceAccount(name string) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } +} + +// ServiceAccountWithControllerRef ... TODO +func (c *Catalog) ServiceAccountWithControllerRef(name string) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + OwnerReferences: []metav1.OwnerReference{ + { + Name: "ss", + Kind: "BuildRun", + }, + }, + }, + } +} + +// DefaultClusterBuildStrategy returns a minimal ClusterBuildStrategy +// object with a inmutable name +func (c *Catalog) DefaultClusterBuildStrategy() *build.ClusterBuildStrategy { + return &build.ClusterBuildStrategy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foobar", + }, + } +} + +// DefaultNamespacedBuildStrategy returns a minimal BuildStrategy +// object with a inmutable name +func (c *Catalog) DefaultNamespacedBuildStrategy() *build.BuildStrategy { + return &build.BuildStrategy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foobar", + }, + } +} + +// BuildRunWithSucceededCondition returns a BuildRun with a single condition +// of the type Succeeded +func (c *Catalog) BuildRunWithSucceededCondition() *build.BuildRun { + return &build.BuildRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foobar", + }, + Status: build.BuildRunStatus{ + Conditions: build.Conditions{ + build.Condition{ + Type: build.Succeeded, + Reason: "foobar", + Message: "foo is not bar", + Status: corev1.ConditionUnknown, + }, + }, + }, + } +} + +// BuildRunWithSA returns a customized BuildRun object that defines a +// service account +func (c *Catalog) BuildRunWithSA(buildRunName string, buildName string, saName string) *build.BuildRun { + return &build.BuildRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildRunName, + }, + Spec: build.BuildRunSpec{ + Build: &build.ReferencedBuild{ + Name: buildName, + }, + ServiceAccount: &saName, + }, + Status: build.BuildRunStatus{}, + } +} + +// BuildRunWithoutSA returns a buildrun without serviceAccountName and generate serviceAccount is false +func (c *Catalog) BuildRunWithoutSA(buildRunName string, buildName string) *build.BuildRun { + return &build.BuildRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildRunName, + }, + Spec: build.BuildRunSpec{ + Build: &build.ReferencedBuild{ + Name: buildName, + }, + ServiceAccount: nil, + }, + } +} + +// BuildRunWithSAGenerate returns a customized BuildRun object that defines a +// service account +func (c *Catalog) BuildRunWithSAGenerate(buildRunName string, buildName string) *build.BuildRun { + return &build.BuildRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildRunName, + }, + Spec: build.BuildRunSpec{ + Build: &build.ReferencedBuild{ + Name: buildName, + }, + ServiceAccount: ptr.To[string](".generate"), + }, + } +} + +// LoadCustomResources returns a container set of resources based on cpu and memory +func (c *Catalog) LoadCustomResources(cpu string, mem string) corev1.ResourceRequirements { + return corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse(cpu), + corev1.ResourceMemory: resource.MustParse(mem), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse(cpu), + corev1.ResourceMemory: resource.MustParse(mem), + }, + } +} + +// LoadBuildYAML parses YAML bytes into JSON and from JSON +// into a Build struct +func (c *Catalog) LoadBuildYAML(d []byte) (*build.Build, error) { + b := &build.Build{} + err := yaml.Unmarshal(d, b) + if err != nil { + return nil, err + } + return b, nil +} + +// LoadBuildWithNameAndStrategy returns a populated Build with name and a referenced strategy +func (c *Catalog) LoadBuildWithNameAndStrategy(name string, strategy string, d []byte) (*build.Build, error) { + b := &build.Build{} + err := yaml.Unmarshal(d, b) + if err != nil { + return nil, err + } + b.Name = name + b.Spec.Strategy.Name = strategy + return b, nil +} + +// LoadBuildRunFromBytes returns a populated BuildRun +func (c *Catalog) LoadBuildRunFromBytes(d []byte) (*build.BuildRun, error) { + b := &build.BuildRun{} + err := yaml.Unmarshal(d, b) + if err != nil { + return nil, err + } + return b, nil +} + +// LoadBRWithNameAndRef returns a populated BuildRun with a name and a referenced Build +func (c *Catalog) LoadBRWithNameAndRef(name string, buildName string, d []byte) (*build.BuildRun, error) { + b := &build.BuildRun{} + err := yaml.Unmarshal(d, b) + if err != nil { + return nil, err + } + b.Name = name + b.Spec.Build.Name = buildName + return b, nil +} + +func (c *Catalog) LoadStandAloneBuildRunWithNameAndStrategy(name string, strategy *build.ClusterBuildStrategy, d []byte) (*build.BuildRun, error) { + b := &build.BuildRun{} + err := yaml.Unmarshal(d, b) + if err != nil { + return nil, err + } + b.Name = name + b.Spec.Build.Build.Strategy = build.Strategy{Kind: (*build.BuildStrategyKind)(&strategy.Kind), Name: strategy.Name} + + return b, nil +} + +// LoadBuildStrategyFromBytes returns a populated BuildStrategy +func (c *Catalog) LoadBuildStrategyFromBytes(d []byte) (*build.BuildStrategy, error) { + b := &build.BuildStrategy{} + err := yaml.Unmarshal(d, b) + if err != nil { + return nil, err + } + return b, nil +} + +// LoadCBSWithName returns a populated ClusterBuildStrategy with a name +func (c *Catalog) LoadCBSWithName(name string, d []byte) (*build.ClusterBuildStrategy, error) { + b := &build.ClusterBuildStrategy{} + err := yaml.Unmarshal(d, b) + if err != nil { + return nil, err + } + b.Name = name + return b, nil +} diff --git a/test/v1beta1_samples/clusterbuildstrategy_samples.go b/test/v1beta1_samples/clusterbuildstrategy_samples.go new file mode 100644 index 000000000..2f9a6a22f --- /dev/null +++ b/test/v1beta1_samples/clusterbuildstrategy_samples.go @@ -0,0 +1,358 @@ +// Copyright The Shipwright Contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package testbeta + +// MinimalBuildahClusterBuildStrategy defines a +// BuildStrategy for Buildah with two steps +// each of them with different container resources +const MinimalBuildahClusterBuildStrategy = ` +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + name: buildah +spec: + volumes: + - name: buildah-images + volumeSource: + emptyDir: {} + buildSteps: + - name: buildah-bud + image: quay.io/containers/buildah:v1.31.0 + workingDir: $(params.shp-source-root) + securityContext: + privileged: true + command: + - /usr/bin/buildah + args: + - bud + - --tag=$(params.shp-output-image) + - --file=$(build.dockerfile) + - $(params.shp-source-context) + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 500m + memory: 1Gi + volumeMounts: + - name: buildah-images + mountPath: /var/lib/containers/storage + - name: buildah-push + image: quay.io/containers/buildah:v1.31.0 + securityContext: + privileged: true + command: + - /usr/bin/buildah + args: + - push + - --tls-verify=false + - docker://$(params.shp-output-image) + resources: + limits: + cpu: 100m + memory: 65Mi + requests: + cpu: 100m + memory: 65Mi + volumeMounts: + - name: buildah-images + mountPath: /var/lib/containers/storage +` + +// ClusterBuildStrategySingleStep defines a +// BuildStrategy for Buildah with a single step +// and container resources +const ClusterBuildStrategySingleStep = ` +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: buildah +spec: + volumes: + - name: buildah-images + volumeSource: + emptyDir: {} + buildSteps: + - name: buildah-bud + image: quay.io/containers/buildah:v1.31.0 + workingDir: $(params.shp-source-root) + securityContext: + privileged: true + command: + - /usr/bin/buildah + args: + - bud + - --tag=$(params.shp-output-image) + - --file=$(build.dockerfile) + - $(params.shp-source-context) + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + volumeMounts: + - name: buildah-images + mountPath: /var/lib/containers/storage + - name: buildah-push + image: quay.io/containers/buildah:v1.31.0 + securityContext: + privileged: true + command: + - /usr/bin/buildah + args: + - push + - --tls-verify=false + - docker://$(params.shp-output-image) + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + volumeMounts: + - name: buildah-images + mountPath: /var/lib/containers/storage +` + +// ClusterBuildStrategySingleStepKaniko is a cluster build strategy based on +// Kaniko, which is very close to the actual Kaniko build strategy example in +// the project +const ClusterBuildStrategySingleStepKaniko = ` +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: kaniko +spec: + buildSteps: + - name: step-build-and-push + image: gcr.io/kaniko-project/executor:v1.15.0 + workingDir: $(params.shp-source-root) + securityContext: + runAsUser: 0 + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - SETFCAP + - KILL + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + command: + - /kaniko/executor + args: + - --skip-tls-verify=true + - --dockerfile=$(build.dockerfile) + - --context=$(params.shp-source-context) + - --destination=$(params.shp-output-image) + - --snapshot-mode=redo + - --push-retry=3 + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi +` + +// ClusterBuildStrategySingleStepKanikoError is a Kaniko based cluster build +// strategy that has a configuration error (misspelled command flag) so that +// it will fail in Tekton +const ClusterBuildStrategySingleStepKanikoError = ` +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: kaniko +spec: + buildSteps: + - name: step-build-and-push + image: gcr.io/kaniko-project/executor:v1.15.0 + workingDir: $(params.shp-source-root) + securityContext: + runAsUser: 0 + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - SETFCAP + - KILL + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + command: + - /kaniko/executor + args: + - --skips-tlss-verifys=true + - --dockerfile=$(build.dockerfile) + - --context=$(params.shp-source-context) + - --destination=$(params.shp-output-image) + - --snapshot-mode=redo + - --push-retry=3 + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi +` + +// ClusterBuildStrategyNoOp is a strategy that does nothing and has no dependencies +const ClusterBuildStrategyNoOp = ` +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: noop +spec: + parameters: + - name: exit-command + description: "Exit command for the pod" + default: "true" + buildSteps: + - name: step-no-and-op + image: alpine:latest + workingDir: $(params.shp-source-root) + securityContext: + runAsUser: 0 + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - SETFCAP + - KILL + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + command: + - $(params.exit-command) + resources: + limits: + cpu: 250m + memory: 128Mi + requests: + cpu: 250m + memory: 128Mi +` + +// ClusterBuildStrategySleep30s is a strategy that does only sleep 30 seconds +const ClusterBuildStrategySleep30s = ` +apiVersion: build.dev/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: noop +spec: + buildSteps: + - name: sleep30 + image: alpine:latest + command: + - sleep + args: + - "30s" + resources: + limits: + cpu: 250m + memory: 128Mi + requests: + cpu: 250m + memory: 128Mi +` + +// ClusterBuildStrategyWithAnnotations is a cluster build strategy that contains annotations +const ClusterBuildStrategyWithAnnotations = ` +apiVersion: shipwright.io/v1beta1 +kind: ClusterBuildStrategy +metadata: + annotations: + kubernetes.io/ingress-bandwidth: 1M + clusterbuildstrategy.shipwright.io/dummy: aValue + kubectl.kubernetes.io/last-applied-configuration: anotherValue + name: kaniko +spec: + buildSteps: + - name: step-build-and-push + image: gcr.io/kaniko-project/executor:v1.15.0 + workingDir: $(params.shp-source-root) + securityContext: + runAsUser: 0 + capabilities: + add: + - CHOWN + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + - SETFCAP + - KILL + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + command: + - /kaniko/executor + args: + - --skip-tls-verify=true + - --dockerfile=$(build.dockerfile) + - --context=$(params.shp-source-root) + - --destination=$(params.shp-output-image) + - --snapshot-mode=redo + - --push-retry=3 + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi +` + +// ClusterBuildStrategyWithParameters is a strategy that uses a +// sleep command with a value for its spec.parameters +const ClusterBuildStrategyWithParameters = ` +apiVersion: build.dev/v1beta1 +kind: ClusterBuildStrategy +metadata: + name: strategy-with-param +spec: + parameters: + - name: sleep-time + description: "time in seconds for sleeping" + default: "1" + buildSteps: + - name: sleep30 + image: alpine:latest + command: + - sleep + args: + - $(params.sleep-time) +` diff --git a/vendor/k8s.io/utils/pointer/pointer.go b/vendor/k8s.io/utils/pointer/pointer.go index b8103223a..b673a6425 100644 --- a/vendor/k8s.io/utils/pointer/pointer.go +++ b/vendor/k8s.io/utils/pointer/pointer.go @@ -14,12 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Deprecated: Use functions in k8s.io/utils/ptr instead: ptr.To to obtain +// a pointer, ptr.Deref to dereference a pointer, ptr.Equal to compare +// dereferenced pointers. package pointer import ( - "fmt" - "reflect" "time" + + "k8s.io/utils/ptr" ) // AllPtrFieldsNil tests whether all pointer fields in a struct are nil. This is useful when, @@ -28,383 +31,219 @@ import ( // // This function is only valid for structs and pointers to structs. Any other // type will cause a panic. Passing a typed nil pointer will return true. -func AllPtrFieldsNil(obj interface{}) bool { - v := reflect.ValueOf(obj) - if !v.IsValid() { - panic(fmt.Sprintf("reflect.ValueOf() produced a non-valid Value for %#v", obj)) - } - if v.Kind() == reflect.Ptr { - if v.IsNil() { - return true - } - v = v.Elem() - } - for i := 0; i < v.NumField(); i++ { - if v.Field(i).Kind() == reflect.Ptr && !v.Field(i).IsNil() { - return false - } - } - return true -} - -// Int returns a pointer to an int -func Int(i int) *int { - return &i -} +// +// Deprecated: Use ptr.AllPtrFieldsNil instead. +var AllPtrFieldsNil = ptr.AllPtrFieldsNil + +// Int returns a pointer to an int. +var Int = ptr.To[int] // IntPtr is a function variable referring to Int. // -// Deprecated: Use Int instead. +// Deprecated: Use ptr.To instead. var IntPtr = Int // for back-compat // IntDeref dereferences the int ptr and returns it if not nil, or else // returns def. -func IntDeref(ptr *int, def int) int { - if ptr != nil { - return *ptr - } - return def -} +var IntDeref = ptr.Deref[int] // IntPtrDerefOr is a function variable referring to IntDeref. // -// Deprecated: Use IntDeref instead. +// Deprecated: Use ptr.Deref instead. var IntPtrDerefOr = IntDeref // for back-compat // Int32 returns a pointer to an int32. -func Int32(i int32) *int32 { - return &i -} +var Int32 = ptr.To[int32] // Int32Ptr is a function variable referring to Int32. // -// Deprecated: Use Int32 instead. +// Deprecated: Use ptr.To instead. var Int32Ptr = Int32 // for back-compat // Int32Deref dereferences the int32 ptr and returns it if not nil, or else // returns def. -func Int32Deref(ptr *int32, def int32) int32 { - if ptr != nil { - return *ptr - } - return def -} +var Int32Deref = ptr.Deref[int32] // Int32PtrDerefOr is a function variable referring to Int32Deref. // -// Deprecated: Use Int32Deref instead. +// Deprecated: Use ptr.Deref instead. var Int32PtrDerefOr = Int32Deref // for back-compat // Int32Equal returns true if both arguments are nil or both arguments // dereference to the same value. -func Int32Equal(a, b *int32) bool { - if (a == nil) != (b == nil) { - return false - } - if a == nil { - return true - } - return *a == *b -} +var Int32Equal = ptr.Equal[int32] // Uint returns a pointer to an uint -func Uint(i uint) *uint { - return &i -} +var Uint = ptr.To[uint] // UintPtr is a function variable referring to Uint. // -// Deprecated: Use Uint instead. +// Deprecated: Use ptr.To instead. var UintPtr = Uint // for back-compat // UintDeref dereferences the uint ptr and returns it if not nil, or else // returns def. -func UintDeref(ptr *uint, def uint) uint { - if ptr != nil { - return *ptr - } - return def -} +var UintDeref = ptr.Deref[uint] // UintPtrDerefOr is a function variable referring to UintDeref. // -// Deprecated: Use UintDeref instead. +// Deprecated: Use ptr.Deref instead. var UintPtrDerefOr = UintDeref // for back-compat // Uint32 returns a pointer to an uint32. -func Uint32(i uint32) *uint32 { - return &i -} +var Uint32 = ptr.To[uint32] // Uint32Ptr is a function variable referring to Uint32. // -// Deprecated: Use Uint32 instead. +// Deprecated: Use ptr.To instead. var Uint32Ptr = Uint32 // for back-compat // Uint32Deref dereferences the uint32 ptr and returns it if not nil, or else // returns def. -func Uint32Deref(ptr *uint32, def uint32) uint32 { - if ptr != nil { - return *ptr - } - return def -} +var Uint32Deref = ptr.Deref[uint32] // Uint32PtrDerefOr is a function variable referring to Uint32Deref. // -// Deprecated: Use Uint32Deref instead. +// Deprecated: Use ptr.Deref instead. var Uint32PtrDerefOr = Uint32Deref // for back-compat // Uint32Equal returns true if both arguments are nil or both arguments // dereference to the same value. -func Uint32Equal(a, b *uint32) bool { - if (a == nil) != (b == nil) { - return false - } - if a == nil { - return true - } - return *a == *b -} +var Uint32Equal = ptr.Equal[uint32] // Int64 returns a pointer to an int64. -func Int64(i int64) *int64 { - return &i -} +var Int64 = ptr.To[int64] // Int64Ptr is a function variable referring to Int64. // -// Deprecated: Use Int64 instead. +// Deprecated: Use ptr.To instead. var Int64Ptr = Int64 // for back-compat // Int64Deref dereferences the int64 ptr and returns it if not nil, or else // returns def. -func Int64Deref(ptr *int64, def int64) int64 { - if ptr != nil { - return *ptr - } - return def -} +var Int64Deref = ptr.Deref[int64] // Int64PtrDerefOr is a function variable referring to Int64Deref. // -// Deprecated: Use Int64Deref instead. +// Deprecated: Use ptr.Deref instead. var Int64PtrDerefOr = Int64Deref // for back-compat // Int64Equal returns true if both arguments are nil or both arguments // dereference to the same value. -func Int64Equal(a, b *int64) bool { - if (a == nil) != (b == nil) { - return false - } - if a == nil { - return true - } - return *a == *b -} +var Int64Equal = ptr.Equal[int64] // Uint64 returns a pointer to an uint64. -func Uint64(i uint64) *uint64 { - return &i -} +var Uint64 = ptr.To[uint64] // Uint64Ptr is a function variable referring to Uint64. // -// Deprecated: Use Uint64 instead. +// Deprecated: Use ptr.To instead. var Uint64Ptr = Uint64 // for back-compat // Uint64Deref dereferences the uint64 ptr and returns it if not nil, or else // returns def. -func Uint64Deref(ptr *uint64, def uint64) uint64 { - if ptr != nil { - return *ptr - } - return def -} +var Uint64Deref = ptr.Deref[uint64] // Uint64PtrDerefOr is a function variable referring to Uint64Deref. // -// Deprecated: Use Uint64Deref instead. +// Deprecated: Use ptr.Deref instead. var Uint64PtrDerefOr = Uint64Deref // for back-compat // Uint64Equal returns true if both arguments are nil or both arguments // dereference to the same value. -func Uint64Equal(a, b *uint64) bool { - if (a == nil) != (b == nil) { - return false - } - if a == nil { - return true - } - return *a == *b -} +var Uint64Equal = ptr.Equal[uint64] // Bool returns a pointer to a bool. -func Bool(b bool) *bool { - return &b -} +var Bool = ptr.To[bool] // BoolPtr is a function variable referring to Bool. // -// Deprecated: Use Bool instead. +// Deprecated: Use ptr.To instead. var BoolPtr = Bool // for back-compat // BoolDeref dereferences the bool ptr and returns it if not nil, or else // returns def. -func BoolDeref(ptr *bool, def bool) bool { - if ptr != nil { - return *ptr - } - return def -} +var BoolDeref = ptr.Deref[bool] // BoolPtrDerefOr is a function variable referring to BoolDeref. // -// Deprecated: Use BoolDeref instead. +// Deprecated: Use ptr.Deref instead. var BoolPtrDerefOr = BoolDeref // for back-compat // BoolEqual returns true if both arguments are nil or both arguments // dereference to the same value. -func BoolEqual(a, b *bool) bool { - if (a == nil) != (b == nil) { - return false - } - if a == nil { - return true - } - return *a == *b -} +var BoolEqual = ptr.Equal[bool] // String returns a pointer to a string. -func String(s string) *string { - return &s -} +var String = ptr.To[string] // StringPtr is a function variable referring to String. // -// Deprecated: Use String instead. +// Deprecated: Use ptr.To instead. var StringPtr = String // for back-compat // StringDeref dereferences the string ptr and returns it if not nil, or else // returns def. -func StringDeref(ptr *string, def string) string { - if ptr != nil { - return *ptr - } - return def -} +var StringDeref = ptr.Deref[string] // StringPtrDerefOr is a function variable referring to StringDeref. // -// Deprecated: Use StringDeref instead. +// Deprecated: Use ptr.Deref instead. var StringPtrDerefOr = StringDeref // for back-compat // StringEqual returns true if both arguments are nil or both arguments // dereference to the same value. -func StringEqual(a, b *string) bool { - if (a == nil) != (b == nil) { - return false - } - if a == nil { - return true - } - return *a == *b -} +var StringEqual = ptr.Equal[string] // Float32 returns a pointer to a float32. -func Float32(i float32) *float32 { - return &i -} +var Float32 = ptr.To[float32] // Float32Ptr is a function variable referring to Float32. // -// Deprecated: Use Float32 instead. +// Deprecated: Use ptr.To instead. var Float32Ptr = Float32 // Float32Deref dereferences the float32 ptr and returns it if not nil, or else // returns def. -func Float32Deref(ptr *float32, def float32) float32 { - if ptr != nil { - return *ptr - } - return def -} +var Float32Deref = ptr.Deref[float32] // Float32PtrDerefOr is a function variable referring to Float32Deref. // -// Deprecated: Use Float32Deref instead. +// Deprecated: Use ptr.Deref instead. var Float32PtrDerefOr = Float32Deref // for back-compat // Float32Equal returns true if both arguments are nil or both arguments // dereference to the same value. -func Float32Equal(a, b *float32) bool { - if (a == nil) != (b == nil) { - return false - } - if a == nil { - return true - } - return *a == *b -} +var Float32Equal = ptr.Equal[float32] // Float64 returns a pointer to a float64. -func Float64(i float64) *float64 { - return &i -} +var Float64 = ptr.To[float64] // Float64Ptr is a function variable referring to Float64. // -// Deprecated: Use Float64 instead. +// Deprecated: Use ptr.To instead. var Float64Ptr = Float64 // Float64Deref dereferences the float64 ptr and returns it if not nil, or else // returns def. -func Float64Deref(ptr *float64, def float64) float64 { - if ptr != nil { - return *ptr - } - return def -} +var Float64Deref = ptr.Deref[float64] // Float64PtrDerefOr is a function variable referring to Float64Deref. // -// Deprecated: Use Float64Deref instead. +// Deprecated: Use ptr.Deref instead. var Float64PtrDerefOr = Float64Deref // for back-compat // Float64Equal returns true if both arguments are nil or both arguments // dereference to the same value. -func Float64Equal(a, b *float64) bool { - if (a == nil) != (b == nil) { - return false - } - if a == nil { - return true - } - return *a == *b -} +var Float64Equal = ptr.Equal[float64] // Duration returns a pointer to a time.Duration. -func Duration(d time.Duration) *time.Duration { - return &d -} +var Duration = ptr.To[time.Duration] // DurationDeref dereferences the time.Duration ptr and returns it if not nil, or else // returns def. -func DurationDeref(ptr *time.Duration, def time.Duration) time.Duration { - if ptr != nil { - return *ptr - } - return def -} +var DurationDeref = ptr.Deref[time.Duration] // DurationEqual returns true if both arguments are nil or both arguments // dereference to the same value. -func DurationEqual(a, b *time.Duration) bool { - if (a == nil) != (b == nil) { - return false - } - if a == nil { - return true - } - return *a == *b -} +var DurationEqual = ptr.Equal[time.Duration] diff --git a/vendor/k8s.io/utils/ptr/OWNERS b/vendor/k8s.io/utils/ptr/OWNERS new file mode 100644 index 000000000..0d6392752 --- /dev/null +++ b/vendor/k8s.io/utils/ptr/OWNERS @@ -0,0 +1,10 @@ +# See the OWNERS docs at https://go.k8s.io/owners + +approvers: +- apelisse +- stewart-yu +- thockin +reviewers: +- apelisse +- stewart-yu +- thockin diff --git a/vendor/k8s.io/utils/ptr/README.md b/vendor/k8s.io/utils/ptr/README.md new file mode 100644 index 000000000..2ca8073dc --- /dev/null +++ b/vendor/k8s.io/utils/ptr/README.md @@ -0,0 +1,3 @@ +# Pointer + +This package provides some functions for pointer-based operations. diff --git a/vendor/k8s.io/utils/ptr/ptr.go b/vendor/k8s.io/utils/ptr/ptr.go new file mode 100644 index 000000000..659ed3b9e --- /dev/null +++ b/vendor/k8s.io/utils/ptr/ptr.go @@ -0,0 +1,73 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 ptr + +import ( + "fmt" + "reflect" +) + +// AllPtrFieldsNil tests whether all pointer fields in a struct are nil. This is useful when, +// for example, an API struct is handled by plugins which need to distinguish +// "no plugin accepted this spec" from "this spec is empty". +// +// This function is only valid for structs and pointers to structs. Any other +// type will cause a panic. Passing a typed nil pointer will return true. +func AllPtrFieldsNil(obj interface{}) bool { + v := reflect.ValueOf(obj) + if !v.IsValid() { + panic(fmt.Sprintf("reflect.ValueOf() produced a non-valid Value for %#v", obj)) + } + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return true + } + v = v.Elem() + } + for i := 0; i < v.NumField(); i++ { + if v.Field(i).Kind() == reflect.Ptr && !v.Field(i).IsNil() { + return false + } + } + return true +} + +// To returns a pointer to the given value. +func To[T any](v T) *T { + return &v +} + +// Deref dereferences ptr and returns the value it points to if no nil, or else +// returns def. +func Deref[T any](ptr *T, def T) T { + if ptr != nil { + return *ptr + } + return def +} + +// Equal returns true if both arguments are nil or both arguments +// dereference to the same value. +func Equal[T comparable](a, b *T) bool { + if (a == nil) != (b == nil) { + return false + } + if a == nil { + return true + } + return *a == *b +} diff --git a/vendor/k8s.io/utils/trace/trace.go b/vendor/k8s.io/utils/trace/trace.go index a0b07a6d7..187eb5d8c 100644 --- a/vendor/k8s.io/utils/trace/trace.go +++ b/vendor/k8s.io/utils/trace/trace.go @@ -65,6 +65,11 @@ func durationToMilliseconds(timeDuration time.Duration) int64 { } type traceItem interface { + // rLock must be called before invoking time or writeItem. + rLock() + // rUnlock must be called after processing the item is complete. + rUnlock() + // time returns when the trace was recorded as completed. time() time.Time // writeItem outputs the traceItem to the buffer. If stepThreshold is non-nil, only output the @@ -79,6 +84,10 @@ type traceStep struct { fields []Field } +// rLock doesn't need to do anything because traceStep instances are immutable. +func (s traceStep) rLock() {} +func (s traceStep) rUnlock() {} + func (s traceStep) time() time.Time { return s.stepTime } @@ -106,6 +115,14 @@ type Trace struct { traceItems []traceItem } +func (t *Trace) rLock() { + t.lock.RLock() +} + +func (t *Trace) rUnlock() { + t.lock.RUnlock() +} + func (t *Trace) time() time.Time { if t.endTime != nil { return *t.endTime @@ -231,8 +248,10 @@ func (t *Trace) logTrace() { func (t *Trace) writeTraceSteps(b *bytes.Buffer, formatter string, stepThreshold *time.Duration) { lastStepTime := t.startTime for _, stepOrTrace := range t.traceItems { + stepOrTrace.rLock() stepOrTrace.writeItem(b, formatter, lastStepTime, stepThreshold) lastStepTime = stepOrTrace.time() + stepOrTrace.rUnlock() } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 51a4da31d..57814a08d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1049,7 +1049,7 @@ k8s.io/kube-openapi/pkg/validation/spec # k8s.io/kubectl v0.26.9 ## explicit; go 1.19 k8s.io/kubectl/pkg/scheme -# k8s.io/utils v0.0.0-20230209194617-a36077c30491 +# k8s.io/utils v0.0.0-20230726121419-3b25d923346b ## explicit; go 1.18 k8s.io/utils/buffer k8s.io/utils/clock @@ -1058,6 +1058,7 @@ k8s.io/utils/integer k8s.io/utils/internal/third_party/forked/golang/net k8s.io/utils/net k8s.io/utils/pointer +k8s.io/utils/ptr k8s.io/utils/strings/slices k8s.io/utils/trace # knative.dev/pkg v0.0.0-20230221145627-8efb3485adcf