From 3b0091c9a6913e95dc3e9dba83cbc681ac8a5d68 Mon Sep 17 00:00:00 2001 From: Adam Kaplan Date: Mon, 23 May 2022 19:02:38 -0400 Subject: [PATCH] Build, Deploy, and Test with ko - Update Makefile to use `go install` instead of `go get` - Use script to install kustomize (go install not supported) - Update manifests to use ko-enabled image references - Add make target to install ko, update Makefile to build and deploy with ko. - Fix namings - Use label shipwright.io/component, value "triggers" to group the deployment and services together. - Use shipwright-triggers as prefix - Rename deployment to "controller" - Add end to end test to ensure the deployment works correctly. - Add documentation on how to build, deploy, and test Triggers in a local development environment. - Add pull request template. - Add GitHub action to lint release notes, draft a release, and run CI tests. --- .github/draft_release_notes.sh | 141 +++++++++++++++++++ .github/pull_request_template.md | 52 +++++++ .github/workflows/ci.yaml | 81 +++++++++++ .github/workflows/release-notes-linter.yaml | 40 ++++++ .github/workflows/release.yaml | 55 ++++++++ .gitignore | 3 + Makefile | 93 +++++++----- config/default/kustomization.yaml | 6 +- config/default/manager_auth_proxy_patch.yaml | 2 +- config/default/manager_config_patch.yaml | 2 +- config/manager/manager.yaml | 21 +-- config/rbac/auth_proxy_service.yaml | 6 +- config/rbac/kustomization.yaml | 4 +- config/rbac/role_binding.yaml | 2 +- config/rbac/service_account.yaml | 2 +- docs/development/local-development.md | 91 ++++++++++++ go.mod | 2 +- test/e2e/framework/deployment.go | 24 ++++ test/e2e/suite_test.go | 64 +++++++++ 19 files changed, 629 insertions(+), 62 deletions(-) create mode 100755 .github/draft_release_notes.sh create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/release-notes-linter.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 docs/development/local-development.md create mode 100644 test/e2e/framework/deployment.go create mode 100644 test/e2e/suite_test.go diff --git a/.github/draft_release_notes.sh b/.github/draft_release_notes.sh new file mode 100755 index 00000000..580458c1 --- /dev/null +++ b/.github/draft_release_notes.sh @@ -0,0 +1,141 @@ +#! /bin/bash +# Copyright The Shipwright Contributors +# +# SPDX-License-Identifier: Apache-2.0 + +# this script assumes the GITHUB_TOKEN and PREVIOUS_TAG environment variables have been set; +# it produces a 'Changes.md' file as its final output; +# the file 'last-300-prs-with-release-note.txt' that is produces is intermediate data; it is not +# pruned for now to assist development of the release notes process (we are still curating all this) + +if [ -z ${GITHUB_TOKEN+x} ]; then + echo "Error: GITHUB_TOKEN is not set" +fi +if [ -z ${PREVIOUS_TAG+x} ]; then + echo "Error: PREVIOUS_TAG is not set" +fi + +sudo apt-get -y update +sudo apt-get -y install wget curl git +curl -L https://github.com/github/hub/releases/download/v2.14.2/hub-linux-amd64-2.14.2.tgz | tar xzf - +PWD="$(pwd)" +export PATH=$PWD/hub-linux-amd64-2.14.2/bin:$PATH +git fetch --all --tags --prune --force +echo "# Draft Release changes since ${PREVIOUS_TAG}" > Changes.md +echo > Features.md +echo "## Features" >> Features.md +echo > Fixes.md +echo "## Fixes" >> Fixes.md +echo > API.md +echo "## API Changes" >> API.md +echo > Docs.md +echo "## Docs" >> Docs.md +echo > Misc.md +echo "## Misc" >> Misc.md + +# this effectively gets the commit associated with github.event.inputs.tags +COMMON_ANCESTOR=$(git merge-base $PREVIOUS_TAG HEAD) +echo "COMMON_ANCESTOR is ${COMMON_ANCESTOR}" +# in theory the new tag has not been created yet; do we want another input that specifies the existing +# commit desired for drafting the release? for now, we are using HEAD in the above git merge-base call +# and PR cross referencing below + +# use of 'hub', which is an extension of the 'git' CLI, allows for pulling of PRs, though we can't search based on commits +# associated with those PRs, so we grab a super big number, 300, which should guarantee grabbing all the PRs back to +# github.events.inputs.tags; we use grep -v to filter out release-note-none. +# NOTE: investigated using the new 'gh' cli command, but its 'gh pr list' does not currently support the -f option so +# staying with 'hub' for now. +hub pr list --state merged -L 300 -f "%sm;%au;%i;%t;%L%n" | grep -E ", release-note|release-note," | grep -v release-note-none > last-300-prs-with-release-note.txt +# this is for debug while we sort out env differences between Gabe's fedora and GitHub Actions' ubuntu +echo "start dump last-300-prs-with-release-note.txt for potential debug" +cat last-300-prs-with-release-note.txt +echo "end dump last-300-prs-with-release-note.txt for potential debug" +# now we cylce through last-300-prs-with-release-note.txt, filtering out stuff that is too old or other anomalies, +# and update Changes.md with the release note. +while IFS= read -r pr; do + SHA=$(echo $pr | cut -d';' -f1) + + # skip the common ancestor, which in essences is the commit associated with the tag github.event.inputs.tags + if [ "$SHA" == "$COMMON_ANCESTOR" ]; then + continue + fi + + # stylistic clarification, purposefully avoiding slicker / cleverer / more compact scripting conventions + + # this makes sure that this PR has merged + git merge-base --is-ancestor $SHA HEAD + rc=$? + if [ ${rc} -eq 1 ]; then + continue + fi + # otherwise, if the current commit from the last 300 PRs is not an ancestor of github.event.inputs.tags, we have gone too far, so skip + git merge-base --is-ancestor $COMMON_ANCESTOR $SHA + rc=$? + if [ ${rc} -eq 1 ]; then + continue + fi + # if we are at this point, we have a PR with a release note to add + AUTHOR=$(echo $pr | cut -d';' -f2) + PR_NUM=$(echo $pr | cut -d';' -f3) + echo "Examining from @${AUTHOR} PR ${PR_NUM}" + PR_BODY=$(wget -q -O- https://api.github.com/repos/shipwright-io/triggers/issues/${PR_NUM:1}) + echo $PR_BODY | grep -oPz '(?s)(?<=```release-note..)(.+?)(?=```)' > /dev/null 2>&1 + rc=$? + if [ ${rc} -eq 1 ]; then + echo "First validation: the release-note field for PR ${PR_NUM} was not properly formatted. Until it is fixed, it will be skipped for release note inclusion." + echo "See the PR template at https://raw.githubusercontent.com/shipwright-io/triggers/master/.github/pull_request_template.md for verification steps" + continue + fi + PR_BODY_FILTER_ONE=$(echo $PR_BODY | grep -oPz '(?s)(?<=```release-note..)(.+?)(?=```)') + echo $PR_BODY_FILTER_ONE | grep -avP '\W*(Your release note here|NONE)\W*' > /dev/null 2>&1 + rc=$? + if [ ${rc} -eq 1 ]; then + echo "Second validation: the release-note field for PR ${PR_NUM} was not properly formatted. Until it is fixed, it will be skipped for release note inclusion." + echo "See the PR template at https://raw.githubusercontent.com/shipwright-io/triggers/master/.github/pull_request_template.md for verification steps" + continue + fi + PR_RELEASE_NOTE=$(echo $PR_BODY_FILTER_ONE | grep -avP '\W*(Your release note here|NONE)\W*') + PR_RELEASE_NOTE_NO_NEWLINES=$(echo $PR_RELEASE_NOTE | sed 's/\\n//g' | sed 's/\\r//g') + MISC=yes + echo $pr | grep 'kind/bug' + rc=$? + if [ ${rc} -eq 0 ]; then + echo >> Fixes.md + echo "$PR_NUM by @$AUTHOR: $PR_RELEASE_NOTE_NO_NEWLINES" >> Fixes.md + MISC=no + fi + echo $pr | grep 'kind/api-change' + rc=$? + if [ ${rc} -eq 0 ]; then + echo >> API.md + echo "$PR_NUM by @$AUTHOR: $PR_RELEASE_NOTE_NO_NEWLINES" >> API.md + MISC=no + fi + echo $pr | grep 'kind/feature' + rc=$? + if [ ${rc} -eq 0 ]; then + echo >> Features.md + echo "$PR_NUM by @$AUTHOR: $PR_RELEASE_NOTE_NO_NEWLINES" >> Features.md + MISC=no + fi + echo $pr | grep 'kind/documentation' + rc=$? + if [ ${rc} -eq 0 ]; then + echo >> Docs.md + echo "$PR_NUM by @$AUTHOR: $PR_RELEASE_NOTE_NO_NEWLINES" >> Docs.md + MISC=no + fi + if [ "$MISC" == "yes" ]; then + echo >> Misc.md + echo "$PR_NUM by @$AUTHOR: $PR_RELEASE_NOTE_NO_NEWLINES" >> Misc.md + fi + # update the PR template if our greps etc. for pulling the release note changes + #PR_RELEASE_NOTE=$(wget -q -O- https://api.github.com/repos/shipwright-io/triggers/issues/${PR_NUM:1} | grep -oPz '(?s)(?<=```release-note..)(.+?)(?=```)' | grep -avP '\W*(Your release note here|action required: your release note here|NONE)\W*') + echo "Added from @${AUTHOR} PR ${PR_NUM:1} to the release note draft" +done < last-300-prs-with-release-note.txt + +cat Features.md >> Changes.md +cat Fixes.md >> Changes.md +cat API.md >> Changes.md +cat Docs.md >> Changes.md +cat Misc.md >> Changes.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..9c0c0198 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,52 @@ +# Changes + + + + + + + +# Submitter Checklist + +- [ ] Includes tests if functionality changed/was added +- [ ] Includes docs if changes are user-facing +- [ ] [Set a kind label on this PR](https://prow.k8s.io/command-help#kind) +- [ ] Release notes block has been filled in, or marked NONE + +See [the contributor guide](https://github.com/shipwright-io/build/blob/main/CONTRIBUTING.md) +for details on coding conventions, github and prow interactions, and the code review process. + +# Release Notes + + diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..10524d68 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,81 @@ +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] +name: ci/github + +permissions: + contents: read + +jobs: + # test: + # strategy: + # matrix: + # go-version: [1.17.x] + # os: [ubuntu-latest] + # runs-on: ${{ matrix.os }} + # steps: + # - name: Install Go + # uses: actions/setup-go@v2 + # with: + # go-version: ${{ matrix.go-version }} + # - name: Check out code + # uses: actions/checkout@v2 + # - name: Unit Test + # run: make test + # - name: Integration Test + # run: make test-integration + e2e: + strategy: + matrix: + kubernetes: [v1.21.2] + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: 1.17.x + - name: Check out code + uses: actions/checkout@v2 + - name: Install kubectl + uses: azure/setup-kubectl@v1 + with: + version: ${{ matrix.kubernetes }} + - name: Install Ko + # This sha corresponds to v0.4 + uses: imjasonh/setup-ko@2c3450ca27f6e6f2b02e72a40f2163c281a1f675 + with: + version: v0.11.2 + - name: Create kind cluster + uses: helm/kind-action@v1.2.0 + with: + version: v0.11.1 + node_image: kindest/node:${{ matrix.kubernetes }} + cluster_name: kind + config: test/e2e/testdata/kind.yaml + wait: 120s + - name: Verify kind cluster + run: | + echo "# Using KinD context..." + kubectl config use-context "kind-kind" + echo "# KinD nodes:" + kubectl get nodes + NODE_STATUS=$(kubectl get node kind-control-plane -o json | jq -r .'status.conditions[] | select(.type == "Ready") | .status') + if [ "${NODE_STATUS}" != "True" ]; then + echo "# Node is not ready:" + kubectl describe node kind-control-plane + echo "# Pods:" + kubectl get pod -A + echo "# Events:" + kubectl get events -A + exit 1 + fi + - name: Install Shipwight Build + run: | + kubectl apply -f https://github.com/shipwright-io/build/releases/download/v0.9.0/release.yaml + kubectl apply -f https://github.com/shipwright-io/build/releases/download/v0.9.0/sample-strategies.yaml + - name: Deploy + run: make deploy KO=/usr/local/bin/ko IMAGE_REPO=kind.local + - name: Test + run: make test-e2e \ No newline at end of file diff --git a/.github/workflows/release-notes-linter.yaml b/.github/workflows/release-notes-linter.yaml new file mode 100644 index 00000000..4fabce54 --- /dev/null +++ b/.github/workflows/release-notes-linter.yaml @@ -0,0 +1,40 @@ +--- +name: Release Note Linter + +on: + pull_request: + branches: + - main + +jobs: + build: + name: Release Note Linter + runs-on: ubuntu-latest + steps: + - name: Sanity Check Release Notes + if: github.actor != 'dependabot[bot]' + env: + PR_NUMBER: ${{ github.event.number }} + run: | + # Validate PR release notes + echo "Going to validate PR ${PR_NUMBER}" + + echo "First making sure you have not left the PR template as is" + # Describe any user facing changes here, or delete this block. + TEMPLATE_LEFT_AS_IS=$(wget -q -O- https://api.github.com/repos/shipwright-io/triggers/pulls/${PR_NUMBER} | jq '.body | match("(Describe any user facing changes here, or delete this block)")') + if [ -z "${TEMPLATE_LEFT_AS_IS}" ]; then + echo "You appear to have attempted to update the PR template to define a release note." + else + echo "You have not made any changes for release notes in your PR description. Edit your PR description per the instructions at https://raw.githubusercontent.com/shipwright-io/triggers/main/.github/pull_request_template.md" + exit 1 + fi + + echo "Now checking against valid structure for release notes" + MATCHES=$(wget -q -O- https://api.github.com/repos/shipwright-io/triggers/pulls/${PR_NUMBER} | jq '.body | match("(```release-note\r\n(.*|NONE|action required: .*)\r\n```)")') + if [ -z "${MATCHES}" ]; then + echo "Your Release Notes were not properly defined or they are not in place, please make sure you add them." + echo "See our PR template for more information: https://raw.githubusercontent.com/shipwright-io/triggers/main/.github/pull_request_template.md" + exit 1 + else + echo "Your Release Notes are properly in place!" + fi diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..b0e03afb --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,55 @@ +name: Release + +on: + workflow_dispatch: + inputs: + release: + description: 'Desired tag' + required: true + tags: + description: 'Previous tag' + required: true + +jobs: + release: + runs-on: ubuntu-latest + permissions: + id-token: write # To be able to get OIDC ID token to sign images. + contents: write # To be able to update releases. + packages: write # To be able to push images and signatures. + pull-requests: write # To be able to create pull requests + env: + IMAGE_HOST: ghcr.io + IMAGE_NAMESPACE: ${{ github.repository }} + TAG: ${{ github.event.inputs.release }} + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Fetch all history, needed for release note generation. + - uses: actions/setup-go@v2 + with: + go-version: 1.17.x + + - name: Build Release Changelog + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PREVIOUS_TAG: ${{ github.event.inputs.tags }} + run: | + # might not be necessary but make sure + chmod +x "${GITHUB_WORKSPACE}/.github/draft_release_notes.sh" + export GITHUB_TOKEN + export PREVIOUS_TAG + "${GITHUB_WORKSPACE}/.github/draft_release_notes.sh" + + - name: Draft release + id: draft_release + uses: actions/create-release@v1 + with: + release_name: "${{ github.event.inputs.release }}" + tag_name: ${{ github.event.inputs.release }} + body_path: Changes.md + draft: true + prerelease: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 66fd13c9..9460581e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +# Binaries for development +bin/ diff --git a/Makefile b/Makefile index 5592f2c9..6cf96e54 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,18 @@ -# Image URL to use all building/pushing image targets -IMG ?= controller:latest +# Image repository hosting the container image +# Container images produced by ko will have the package appended to the path +IMAGE_REPO ?= ghcr.io/shipwright-io/triggers + +# Tag to use when building the container image +TAG ?= latest + +# Format for the SBOM produced by ko. +# Defaults to "spdx", use "none" to disable SBOM generation +SBOM ?= "spdx" + +# Pass in options directly to ko +KO_OPTS ?= -B -t ${TAG} --sbom=${SBOM} + # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.23 @@ -55,9 +67,14 @@ fmt: ## Run go fmt against code. vet: ## Run go vet against code. go vet ./... -.PHONY: test -test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out +# .PHONY: test +# test: manifests generate fmt vet envtest ## Run tests. +# KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out + +.PHONY: test-e2e +test-e2e: ## Run e2e tests. + KUBECONFIG=${KUBECONFIG} go test -v ./test/e2e/... + ##@ Build @@ -69,13 +86,13 @@ build: generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go -.PHONY: docker-build -docker-build: test ## Build docker image with the manager. - docker build -t ${IMG} . +.PHONY: container-build +container-build: test ko ## Build the container image with the manager. + KO_DOCKER_REPO=${IMAGE_REPO} $(KO) build . --push=false ${KO_OPTS} -.PHONY: docker-push -docker-push: ## Push docker image with the manager. - docker push ${IMG} +.PHONY: container-push +container-push: ko ## Push the container image with the manager. + KO_DOCKER_REPO=${IMAGE_REPO} $(KO) build . ${KO_OPTS} ##@ Deployment @@ -92,39 +109,45 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - .PHONY: deploy -deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default | kubectl apply -f - +deploy: manifests kustomize ko ## Build and deploy the controller to the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/default | KO_DOCKER_REPO=${IMAGE_REPO} $(KO) apply ${KO_OPTS} -f - .PHONY: undeploy undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - -CONTROLLER_GEN = $(shell pwd)/bin/controller-gen +##@ Build Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): ## Ensure that the directory exists + mkdir -p $(LOCALBIN) + +CONTROLLER_GEN = $(LOCALBIN)/controller-gen +KUSTOMIZE = $(LOCALBIN)/kustomize +ENVTEST = $(LOCALBIN)/setup-envtest +KO = $(LOCALBIN)/ko + +## Tool Versions +KUSTOMIZE_VERSION ?= v3.8.7 +CONTROLLER_TOOLS_VERSION ?= v0.8.0 +KO_VERSION ?= v0.11.2 + .PHONY: controller-gen controller-gen: ## Download controller-gen locally if necessary. - $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0) + GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) -KUSTOMIZE = $(shell pwd)/bin/kustomize +KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize -kustomize: ## Download kustomize locally if necessary. - $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7) +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. +$(KUSTOMIZE): + curl -s $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN) -ENVTEST = $(shell pwd)/bin/setup-envtest .PHONY: envtest envtest: ## Download envtest-setup locally if necessary. - $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) - -# go-get-tool will 'go get' any package $2 and install it to $1. -PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) -define go-get-tool -@[ -f $(1) ] || { \ -set -e ;\ -TMP_DIR=$$(mktemp -d) ;\ -cd $$TMP_DIR ;\ -go mod init tmp ;\ -echo "Downloading $(2)" ;\ -GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\ -rm -rf $$TMP_DIR ;\ -} -endef + GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest + +.PHONY: ko +ko: $(KO) ## Download ko locally if necessary +$(KO): + GOBIN=$(LOCALBIN) go install github.com/google/ko@$(KO_VERSION) diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index b1dd1739..a2aca532 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -1,19 +1,19 @@ # Adds namespace to all resources. -namespace: triggers-system +namespace: shipwright-build # Value of this field is prepended to the # names of all resources, e.g. a deployment named # "wordpress" becomes "alices-wordpress". # Note that it should also match with the prefix (text before '-') of the namespace # field above. -namePrefix: triggers- +namePrefix: shipwright-triggers- # Labels to add to all resources and selectors. #commonLabels: # someName: someValue bases: -- ../crd +# - ../crd - ../rbac - ../manager # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml index 45be3188..d4d3c3a5 100644 --- a/config/default/manager_auth_proxy_patch.yaml +++ b/config/default/manager_auth_proxy_patch.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: controller-manager + name: controller namespace: system spec: template: diff --git a/config/default/manager_config_patch.yaml b/config/default/manager_config_patch.yaml index 6c400155..0926f8b3 100644 --- a/config/default/manager_config_patch.yaml +++ b/config/default/manager_config_patch.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: controller-manager + name: controller namespace: system spec: template: diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index cf11cecc..779fffd5 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -1,37 +1,30 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - name: system ---- apiVersion: apps/v1 kind: Deployment metadata: - name: controller-manager + name: controller namespace: system labels: - control-plane: controller-manager + shipwright.io/component: triggers spec: selector: matchLabels: - control-plane: controller-manager + shipwright.io/component: triggers replicas: 1 template: metadata: annotations: kubectl.kubernetes.io/default-container: manager labels: - control-plane: controller-manager + shipwright.io/component: triggers spec: securityContext: runAsNonRoot: true containers: - command: - - /manager + - /ko-app/triggers args: - --leader-elect - image: controller:latest + image: ko://github.com/shipwright-io/triggers name: manager securityContext: allowPrivilegeEscalation: false @@ -56,5 +49,5 @@ spec: requests: cpu: 10m memory: 64Mi - serviceAccountName: controller-manager + serviceAccountName: controller terminationGracePeriodSeconds: 10 diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml index 71f17972..184f6ec2 100644 --- a/config/rbac/auth_proxy_service.yaml +++ b/config/rbac/auth_proxy_service.yaml @@ -2,8 +2,8 @@ apiVersion: v1 kind: Service metadata: labels: - control-plane: controller-manager - name: controller-manager-metrics-service + shipwright.io/component: triggers + name: controller-metrics-service namespace: system spec: ports: @@ -12,4 +12,4 @@ spec: protocol: TCP targetPort: https selector: - control-plane: controller-manager + shipwright.io/component: triggers diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 731832a6..40fc99ae 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -5,8 +5,8 @@ resources: # runtime. Be sure to update RoleBinding and ClusterRoleBinding # subjects if changing service account names. - service_account.yaml -- role.yaml -- role_binding.yaml +# - role.yaml +# - role_binding.yaml - leader_election_role.yaml - leader_election_role_binding.yaml # Comment the following 4 lines if you want to disable diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index 2070ede4..6a4e9985 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -8,5 +8,5 @@ roleRef: name: manager-role subjects: - kind: ServiceAccount - name: controller-manager + name: controller namespace: system diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml index 7cd6025b..130eeec1 100644 --- a/config/rbac/service_account.yaml +++ b/config/rbac/service_account.yaml @@ -1,5 +1,5 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: controller-manager + name: controller namespace: system diff --git a/docs/development/local-development.md b/docs/development/local-development.md new file mode 100644 index 00000000..0a609c9d --- /dev/null +++ b/docs/development/local-development.md @@ -0,0 +1,91 @@ +# Local Development Guide + +## Building + +### Golang + +This project assumes you have golang 1.17 or higher installed on your machine. + +### Container Image + +This project uses [ko](https://github.com/google/ko) to build the container image with the +controller manager and deploy it to a Kubernetes cluster. To change the destination container +registry, provide the `IMAGE_REPO` variable to any make target: + +```sh +$ make container-build IMAGE_REPO=quay.io/myusername +``` + +Similarly, use the `TAG` variable to modify the tag for the built image. +By default, this repository builds and pushes the controller image to +`ghcr.io/shipwright-io/triggers/triggers:latest`. + +ko by default generates a Software Bill of Materials (SBOM) alongside the container image, which +can be pushed to a container registry. +Not all container registries support SBOM images - to disable this, set the `SBOM` variable to +`none`: + +```sh +$ make container-push IMAGE_REPO=quay.io/myusername SBOM=none +``` + +If you need finer control over how ko builds and pushes images, provide all necessary ko +arguments to the `KO_OPTS` variable: + +```sh +$ make container-build IMAGE_REPO=quay.io/myusername/kubebuilder-plus KO_OPTS="--bare --tag=mytag" +``` + +By default the following arguments are passed to ko: + +- `-B`: this strips the md5 hash from the image name +- `-t ${TAG}`: sets the tag for the image. Defaults to the `TAG` argument (`latest`) +- `--sbom=${SBOM}`: configures SBOM behavior. Defaults to the `SBOM` argument (`spdx`) + +## Deploying + +Use the following instructions to deploy Triggers on a local Kubernetes cluster. + +### KinD + +To deploy on a KinD cluster, do the following: + +1. Install Tekton Pipelines from the latest supported version for Shipwright: + + ```sh + $ kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/previous/v0.34.1/release.yaml + ``` + +2. Install Shipwright Build from the most recent release: + ```sh + $ kubectl apply --filename https://github.com/shipwright-io/build/releases/download/v0.9.0/sample-strategies.yaml + ``` + +3. Deploy Shipwright Triggers using the `kind.local` image repo: + + ```sh + $ make deploy IMAGE_REPO=kind.local + ``` + +### CodeReady Containers + +To deploy on an OpenShift CodeReady Containers cluster, do the following: + +1. Install Shipwright and Tekton using the Shipwright Operator in the Community Operators catalog. Make sure that a `ShipwrightBuild` instance is created and reports itself `Ready=True` in its status conditions. + +2. Use the following options for `make deploy`: + + ```sh + $ make deploy IMAGE_REPO=default-route-openshift-image-registry.apps-crc.testing/shipwright-build KO_OPTS="-B -t latest --insecure-registry" + ``` + +## Testing + +### Unit and Integration Testing + +TODO - add instructions on unit and integration tests when these are ready. + +### End to End Testing + +The end to end test suite is invoked by running `make test-e2e` against a cluster with Triggers deployed. + diff --git a/go.mod b/go.mod index 3733981f..beb33514 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.17 require ( github.com/onsi/ginkgo/v2 v2.1.4 github.com/onsi/gomega v1.19.0 + k8s.io/api v0.23.0 k8s.io/apimachinery v0.23.0 k8s.io/client-go v0.23.0 sigs.k8s.io/controller-runtime v0.11.0 @@ -60,7 +61,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - k8s.io/api v0.23.0 // indirect k8s.io/apiextensions-apiserver v0.23.0 // indirect k8s.io/component-base v0.23.0 // indirect k8s.io/klog/v2 v2.30.0 // indirect diff --git a/test/e2e/framework/deployment.go b/test/e2e/framework/deployment.go new file mode 100644 index 00000000..092845c6 --- /dev/null +++ b/test/e2e/framework/deployment.go @@ -0,0 +1,24 @@ +package framework + +import ( + "context" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func IsDeploymentReady(ctx context.Context, c client.Client) (bool, error) { + deployment := &appsv1.Deployment{} + err := c.Get(ctx, client.ObjectKey{Namespace: "shipwright-build", Name: "shipwright-triggers-controller"}, deployment) + if err != nil { + return false, err + } + for _, condition := range deployment.Status.Conditions { + if condition.Type != appsv1.DeploymentAvailable { + continue + } + return condition.Status == corev1.ConditionTrue, nil + } + return false, nil +} diff --git a/test/e2e/suite_test.go b/test/e2e/suite_test.go new file mode 100644 index 00000000..6fad7387 --- /dev/null +++ b/test/e2e/suite_test.go @@ -0,0 +1,64 @@ +package e2e + +import ( + "context" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/shipwright-io/triggers/test/e2e/framework" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + kubeClient client.Client + testContext context.Context + cancel context.CancelFunc +) + +func TestE2E(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "E2E Suite") +} + +var _ = BeforeSuite(func() { + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + rest, err := ctrl.GetConfig() + Expect(err).NotTo(HaveOccurred()) + + kubeClient, err = client.New(rest, client.Options{ + Scheme: scheme, + }) + Expect(err).NotTo(HaveOccurred()) + testContext, cancel = context.WithCancel(context.Background()) +}) + +var _ = BeforeEach(func() { + By("waiting for project deployment") + err := wait.PollImmediate(10*time.Second, 5*time.Minute, func() (bool, error) { + return framework.IsDeploymentReady(testContext, kubeClient) + }) + Expect(err).NotTo(HaveOccurred()) + +}) + +var _ = When("the controller has been deployed", func() { + It("should be ready", func() { + ready, err := framework.IsDeploymentReady(testContext, kubeClient) + Expect(err).NotTo(HaveOccurred()) + Expect(ready).To(BeTrue()) + }) +}) + +var _ = AfterSuite(func() { + cancel() +})