diff --git a/.github/actions/trigger-run/action.yaml b/.github/actions/trigger-run/action.yaml new file mode 100644 index 0000000..c0471cb --- /dev/null +++ b/.github/actions/trigger-run/action.yaml @@ -0,0 +1,71 @@ +name: "Trigger Terraform Run" +description: "Trigger terraform run on terraform-applier managed modules." + +# Action Envs +# TFA_K8S_API_SERVER: The address and port of the Kubernetes API server (required) + +# TFA_K8S_TOKEN: Bearer token for authentication to the API server (required) + +# TFA_NAMESPACE: The namespace of the module" (required) + +# TFA_MODULE: The name of the module to trigger run (required) + +# TFA_RUN_TYPE: Type of the run to trigger valid options are 'ForcedApply' or 'ForcedPlan' (optional) +# default: ForcedApply + +# TFA_INSECURE: Allow insecure server connections (optional) +# default: false + +runs: + using: "composite" + steps: + - name: Check inputs + shell: bash + run: | + # check all required envs + if [[ -z "$TFA_K8S_API_SERVER" ]]; then + echo "'TFA_K8S_API_SERVER' env not set, the Kubernetes API server address is required" >&2 + fail=true + fi + if [[ -z "$TFA_K8S_TOKEN" ]]; then + echo "'TFA_K8S_TOKEN' env not set, the Kubernetes API token is required" >&2 + fail=true + fi + if [[ -z "$TFA_NAMESPACE" ]]; then + echo "'TFA_NAMESPACE' env not set, the module's namespace name is required" >&2 + fail=true + fi + if [[ -z "$TFA_MODULE" ]]; then + echo "'TFA_MODULE' env not set, the module's name is required" >&2 + fail=true + fi + + # set default value if not set and validate + if [[ -z "$TFA_RUN_TYPE" ]]; then + TFA_RUN_TYPE=ForcedApply + elif [[ $TFA_RUN_TYPE != "ForcedApply" && $TFA_RUN_TYPE != "ForcedPlan" ]]; then + echo "'TFA_RUN_TYPE=$TFA_RUN_TYPE' invalid value set, only 'ForcedApply' or 'ForcedPlan' are allowed as run type" >&2 + fail=true + fi + + if [[ $fail ]]; then + exit 1 + fi + + - name: Trigger Run + shell: bash + run: | + k8s_url="$TFA_K8S_API_SERVER/apis/terraform-applier.uw.systems/v1beta1/namespaces/$TFA_NAMESPACE/modules/$TFA_MODULE" + plan_req=$(printf '{\\"reqAt\\":\\"%s\\",\\"type\\":\\"%s\\"}' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" $TFA_RUN_TYPE) + k8s_payload=$(printf '{"metadata": {"annotations": {"terraform-applier.uw.systems/run-request": "%s"}}}' "$plan_req") + + curl -s --show-error --fail \ + -o /dev/null \ + -w '%{http_code}'\ + --connect-timeout 60 \ + -X PATCH \ + $(if [[ $TFA_INSECURE == "true" ]]; then printf -- '--insecure'; fi) \ + -d "$k8s_payload" \ + -H 'Content-Type: application/merge-patch+json' \ + -H "Authorization: Bearer $TFA_K8S_TOKEN" \ + $k8s_url diff --git a/README.md b/README.md index 0ae986f..75be0bb 100644 --- a/README.md +++ b/README.md @@ -313,3 +313,72 @@ In addition to the [controller-runtime](https://book.kubebuilder.io/reference/me - `terraform_applier_git_last_mirror_timestamp` - (tags: `repo`) A Gauge that captures the Timestamp of the last successful git sync per repo. - `terraform_applier_git_mirror_count` - (tags: `repo`,`success`) A Counter for each repo sync, incremented with each sync attempt and tagged with the result (`success=true|false`) - `terraform_applier_git_mirror_latency_seconds` - (tags: `repo`) A Summary that keeps track of the git sync latency per repo. + + +## Github Actions + +`terraform-applier` provides github action to [trigger runs on managed modules](.github/actions/trigger-run/action.yaml). +`action` uses kubernetes API calls to trigger runs, For authentication it uses service account +token with access to the modules. + +The [ci-rbac](manifests/base/ci-rbac) base can be imported in namespace to provide min required permission +to trigger runs on any modules in the namespace. +base will create `terraform-applier-ci` service account and corresponding secret. + + +Import `ci-rbac` base to create required sa and secret +```yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - github.com/utilitywarehouse/terraform-applier//manifests/base/ci-rbac?ref=master +``` + +To get the bearer token you can find the Secret in k8s. +this token should be added to `Repository secrets` of the repo. +```sh +kubectl --context -n get secrets terraform-applier-ci-token -o json | jq -r '.data.token' | base64 -d +``` + +### Github Workflow + +Following workflow will trigger apply run on `hello` module from `default` namespace +on push to `master` branch. +```yaml +name: trigger-terraform-run + +on: + push: + branches: + - master + - main + +jobs: + trigger-terraform-run: + runs-on: ubuntu-latest # or internal runner if k8s is on private network + steps: + - name: Trigger Terraform Apply + uses: utilitywarehouse/terraform-applier/.github/actions/trigger-run@master + env: + # The address and port of the Kubernetes API server (required) + TFA_K8S_API_SERVER: https://k8s-api-server-url + + # Bearer token for authentication to the API server (required) + # if ci base is used then this is the token from 'terraform-applier-ci-token' secret + TFA_K8S_TOKEN: ${{ secrets.K8S_TOKEN }} + + # The namespace of the module (required) + TFA_NAMESPACE: default + + # The name of the module to trigger run (required) + TFA_MODULE: hello + + # Type of the run to trigger valid options are 'ForcedApply' or 'ForcedPlan'. + # (optional) default: ForcedApply + TFA_RUN_TYPE: ForcedApply + + # Allow insecure server connections (optional) + # default: false + TFA_INSECURE: true +``` \ No newline at end of file diff --git a/manifests/base/ci-rbac/kustomization.yaml b/manifests/base/ci-rbac/kustomization.yaml new file mode 100644 index 0000000..b5633ec --- /dev/null +++ b/manifests/base/ci-rbac/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - rbac.yaml diff --git a/manifests/base/ci-rbac/rbac.yaml b/manifests/base/ci-rbac/rbac.yaml new file mode 100644 index 0000000..4cc064d --- /dev/null +++ b/manifests/base/ci-rbac/rbac.yaml @@ -0,0 +1,36 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: terraform-applier-ci +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: terraform-applier-ci +rules: + - apiGroups: + - terraform-applier.uw.systems + resources: + - modules + verbs: + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: terraform-applier-ci +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: terraform-applier-ci +subjects: + - kind: ServiceAccount + name: terraform-applier-ci +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + kubernetes.io/service-account.name: terraform-applier-ci + name: terraform-applier-ci-token +type: kubernetes.io/service-account-token