diff --git a/.github/Taskfile.yml b/.github/Taskfile.yml new file mode 100644 index 0000000..8514463 --- /dev/null +++ b/.github/Taskfile.yml @@ -0,0 +1,23 @@ +version: "3" + +tasks: + # # Lind and test (local) + + lint: # https://github.com/super-linter/super-linter + desc: "GitHub Super-Linter" + cmds: + - source .github/scripts/test.sh && gh_super_linter + + # # Scan with Checkov (local) + + checkov: # https://www.checkov.io/ + desc: "Checkov, find cloud infra misconfigurations" + cmds: + - source .github/scripts/test.sh && checkov + + # # Scan with Trivy (local) + + trivy: # https://github.com/aquasecurity/trivy + desc: "Trivy security scanner" + cmds: + - source .github/scripts/test.sh && trivy diff --git a/.github/linters/.yaml-lint.yml b/.github/linters/.yaml-lint.yml new file mode 100644 index 0000000..aa5b764 --- /dev/null +++ b/.github/linters/.yaml-lint.yml @@ -0,0 +1,62 @@ +--- +########################################### +# These are the rules used for # +# linting all the yaml files in the stack # +# NOTE: # +# You can disable line with: # +# # yamllint disable-line # +########################################### +rules: + braces: + disable + # level: warning + # min-spaces-inside: 0 + # max-spaces-inside: 0 + # min-spaces-inside-empty: 1 + # max-spaces-inside-empty: 5 + brackets: + disable + # level: warning + # min-spaces-inside: 0 + # max-spaces-inside: 0 + # min-spaces-inside-empty: 1 + # max-spaces-inside-empty: 5 + colons: + level: warning + max-spaces-before: 0 + max-spaces-after: 1 + commas: + level: warning + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: disable + comments-indentation: disable + document-end: disable + document-start: + level: warning + present: true + empty-lines: + level: warning + max: 2 + max-start: 0 + max-end: 0 + hyphens: + level: warning + max-spaces-after: 1 + indentation: + level: warning + spaces: consistent + indent-sequences: true + check-multi-line-strings: false + key-duplicates: enable + line-length: + disable + # level: warning + # max: 180 + # allow-non-breakable-words: true + # allow-non-breakable-inline-mappings: true + new-line-at-end-of-file: disable + new-lines: + type: unix + trailing-spaces: disable diff --git a/.github/readme.md b/.github/readme.md new file mode 100644 index 0000000..9b8cfac --- /dev/null +++ b/.github/readme.md @@ -0,0 +1,13 @@ +# Scripts To Rule Them All + +How CI works at GitHub and locally. + +> Each of these scripts is responsible for a unit of work. + +What is used in this project: + +1. [GitHub Super Linter](https://github.com/github/super-linter/blob/main/docs/run-linter-locally.md) + +2. [Checkov](https://www.checkov.io/) + +3. [Trivy](https://github.com/aquasecurity/trivy) diff --git a/.github/scripts/server.sh b/.github/scripts/server.sh new file mode 100644 index 0000000..af40324 --- /dev/null +++ b/.github/scripts/server.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -e + +# # # WSL # # # + +# Start Docker Desktop if it's not running +if ! docker ps; then + powershell.exe "Start-Process -FilePath 'C:\Program Files\Docker\Docker\Docker Desktop.exe'" + while ! docker ps; do # Wait for Docker to start + echo "==> Docker is starting" + sleep 3 + done +else + echo "==> Docker is running" +fi + +# # # WSL # # # diff --git a/.github/scripts/test.sh b/.github/scripts/test.sh new file mode 100644 index 0000000..1e385ef --- /dev/null +++ b/.github/scripts/test.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -e + +# # Create a bash function to run super-linter +function gh_super_linter() { + # Start docker desktop if it's not running + source "$PWD/.github/scripts/server.sh" + + # Run the docker container + docker run --rm \ + -e RUN_LOCAL=true \ + --env-file ".github/super-linter.env" \ + -v "$PWD":/tmp/lint github/super-linter:slim-v5 +} + +# Create a bash function to run checkov +function checkov() { + # Start docker desktop if it's not running + source "$PWD/.github/scripts/server.sh" + + # Run the docker container + docker run -it --rm \ + -v "$PWD":/tmp/lint --workdir /tmp/lint \ + bridgecrew/checkov \ + --directory /tmp/lint \ + --soft-fail \ + --quiet +} + +# Create a bash function to run trivy +function trivy() { + # Start docker desktop if it's not running + source "$PWD/.github/scripts/server.sh" + + # Run the docker container + docker run -it --rm \ + -v "$PWD":/tmp/lint --workdir /tmp/lint \ + aquasec/trivy:latest \ + fs --scanners vuln,config,secret . +} diff --git a/.github/super-linter.env b/.github/super-linter.env new file mode 100644 index 0000000..fa9ce7f --- /dev/null +++ b/.github/super-linter.env @@ -0,0 +1,3 @@ +VALIDATE_ALL_CODEBASE=true +VALIDATE_KUBERNETES_KUBECONFORM=false +VALIDATE_TERRAFORM_TERRASCAN=false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..52d6717 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +--- +name: CI + +on: + push: + branches: [main] + +permissions: read-all + +jobs: + lint: + # Run GH Super-Linter against code base + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: cat .github/super-linter.env >> "$GITHUB_ENV" + - name: Lint Code Base + uses: super-linter/super-linter/slim@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DEFAULT_BRANCH: main diff --git a/Taskfile.yaml b/Taskfile.yaml index 6bc5aaf..190ee62 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -9,9 +9,9 @@ env: CLUSTER_NAME: kubepal KUBE: export KUBECONFIG=/mnt/c/Users/{{.USER}}/.kube/config # WSL path to kubeconfig file on Windows host -# Include the helper tasks +# Include the github actions tasks includes: - run: utils/helper_tasks.yml + run: .github/Taskfile.yml tasks: create: @@ -25,8 +25,13 @@ tasks: {{ if and (eq OS "linux") (ne (env "KUBECONFIG") "") }}Kubeconfig path: {{(env "KUBECONFIG")}}{{end}} cmds: + # Source the server.sh script to start docker + - '{{if eq OS "linux"}}source .github/scripts/server.sh{{end}}' + # If KUBECONFIG env var is not set, set it to point to the Windows host - - '{{if eq OS "linux"}}[ -z "$KUBECONFIG" ] && echo "Set KUBECONFIG env var ==> {{.KUBE}}" && exit 1 || k3d cluster create {{.CLUSTER_NAME}} --api-port 6443 -p 8080:80@loadbalancer --agents 2{{end}}' + - '{{if eq OS "linux"}}[ -z "$KUBECONFIG" ] && echo "Set KUBECONFIG env var ==> {{.KUBE}}" && exit 1 \ + || k3d cluster create {{.CLUSTER_NAME}} --api-port 6443 -p 8080:80@loadbalancer --agents 2{{end}}' + # If host is Windows, create cluster - '{{if eq OS "windows"}}k3d cluster create {{.CLUSTER_NAME}} --api-port 6443 -p 8080:80@loadbalancer --agents 2{{end}}' diff --git a/docs/kubepal_latest.md b/docs/kubepal_latest.md new file mode 100644 index 0000000..abab920 --- /dev/null +++ b/docs/kubepal_latest.md @@ -0,0 +1,60 @@ +# + +This release focuses on Taskfile tasks and Go templating examples in tasks. + +## Features and Tools + +- `[Task](https://taskfile.dev/):` a cross-platform alternative to Make, supporting and showcasing `Go templates`. + +- `Terraform:` bootstraps the cluster and applies the ArgoCD helm chart. + +## Getting Started + +> **In this example, a WSL distro using a kubeconfig file located in the Windows user's home directory** + +1. Update the `KUBE` variable in [Taskfile.yml](../Taskfile.yaml) to match your path. + + ```yaml + env: + CLUSTER_NAME: kubepal + KUBE: export KUBECONFIG=/mnt/c/Users/{{.USER}}/.kube/config + ``` + +2. Update the `kube/bootstrap/backend.tf` to match `the path of your local kubeconfig file`: + + > Warning: Variables, locals, and functions may not be used here. + + ```hcl + + terraform { + backend "kubernetes" { + secret_suffix = "state" + + config_paths = [ + "/mnt/c/Users/devops/.kube/config", # WSL path + "c:\\Users\\devops\\.kube\\config" # PowerShell path + ] + } + } + + + ``` + +3. Run `task --list` in WSL or PowerShell to see the available tasks and the updated kubeconfig path. + + ```shell + $ task + task: Available tasks for this project: + + * create: Create and access a k3d cluster. + * delete: Delete cluster + * deploy: Bootstrap cluster and deploy apps + * tf_init: Initialize terraform + * run:connect: Get a shell to a running container + * run:pod: Create a temporary interactive pod, install some tools on it, and experiment + * run:start: Go template if statement + * run:test: Go template functions and examples using Powershell and Bash + ``` + +4. The `task deploy` command will create the cluster, deploy ArgoCD, and deploy the apps. +5. The `task delete` command will delete the cluster. diff --git a/kube/base/argocd_chart_values.yml b/kube/base/argocd_chart_values.yml index df5f6e3..365ebf2 100644 --- a/kube/base/argocd_chart_values.yml +++ b/kube/base/argocd_chart_values.yml @@ -364,7 +364,7 @@ configs: # githubAppInstallationID: 2 # githubAppEnterpriseBaseUrl: https://ghe.example.com/api/v3 # githubAppPrivateKey: | - # -----BEGIN OPENSSH PRIVATE KEY----- + # -----BEGIN OPENSSH PRIVATE KEY----- #gitleaks:allow # ... # -----END OPENSSH PRIVATE KEY----- # https-creds: diff --git a/kube/bootstrap/providers.tf b/kube/bootstrap/providers.tf index 1b56bef..e0c93be 100644 --- a/kube/bootstrap/providers.tf +++ b/kube/bootstrap/providers.tf @@ -1,6 +1,18 @@ // Extract the config_path values from the backend.tf file // and use it to configure the kubernetes provider // and the helm provider +terraform { + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = "~> 2.0" + } + helm = { + source = "hashicorp/helm" + version = "~> 2.0" + } + } +} locals { @@ -29,3 +41,4 @@ provider "helm" { } } + diff --git a/kube/bootstrap/tests/argocd.tftest.hcl b/kube/bootstrap/tests/argocd.tftest.hcl new file mode 100644 index 0000000..d4472aa --- /dev/null +++ b/kube/bootstrap/tests/argocd.tftest.hcl @@ -0,0 +1,30 @@ + +run "argocd_helm_release" { + + command = plan + + # Verify ArgoCD helm chart name + assert { + condition = helm_release.argocd.chart == "argo-cd" + error_message = "ArgoCD helm chart did not match expected" + } + + # Verify ArgoCD helm chart repository + assert { + condition = helm_release.argocd.repository == "https://argoproj.github.io/argo-helm" + error_message = "ArgoCD helm chart repository did not match expected" + } + +} + +run "argocd_ingress" { + + command = plan + + # Verify ArgoCD yaml manifest apiVersion + assert { + condition = kubernetes_manifest.argocd_ingress.manifest.apiVersion == "networking.k8s.io/v1" + error_message = "ArgoCD yaml manifest apiVersion did not match expected" + } + +} diff --git a/utils/helper_tasks.yml b/utils/helper_tasks.yml deleted file mode 100644 index bc9cf47..0000000 --- a/utils/helper_tasks.yml +++ /dev/null @@ -1,36 +0,0 @@ -version: "3" - -env: - POD_NAME: kubepal-tester - -tasks: - pod: - desc: "Create a temporary interactive pod, install some tools on it, and experiment " - cmds: - - kubectl run -i --tty {{.POD_NAME}} --image debian - status: - - kubectl get pods {{.POD_NAME}} # If pod exists, skip the task - - connect: - desc: "Get a shell to a running container" - cmds: - - task: pod - - kubectl exec --stdin --tty {{.POD_NAME}} -- /bin/bash - - test: - desc: "Go template functions examples using Powershell and Bash" - cmds: - - '{{if eq OS "windows"}}powershell "Get-ComputerInfo | select WindowsProductName"{{else}}cat /etc/os-release{{end}}' - - - '{{ if and (eq OS "windows") (eq (env "KUBECONFIG") "") }} echo "OS is windows and KUBECONFIG var is empty" {{end}}' - silent: true - - start: - desc: "Start Docker Desktop on Windows" - cmds: - # Run a command if OS is windows - - '{{if eq OS "windows"}}powershell Start-Process "\"C:\ProgramData\Microsoft\Windows\Start Menu\Docker Desktop"\"{{end}}' - - # If OS is linux, echo a message - - '{{if eq OS "linux"}}echo "This is a Windows only command"{{end}}' - silent: true