From ae2af2acb7bc9d015eb0518acd7403db7ed53feb Mon Sep 17 00:00:00 2001 From: Martin Jackson Date: Thu, 21 Nov 2024 08:28:59 -0600 Subject: [PATCH] Intial commit --- .github/linters/.checkov.yaml | 12 ++ .github/linters/.jscpd.json | 10 ++ .github/linters/.textlintrc | 10 ++ .github/workflows/helm-lint.yml | 29 ++++ .github/workflows/helm-unittest.yml | 25 +++ .github/workflows/superlinter.yml | 36 +++++ .github/workflows/update-helm-repo.yml | 30 ++++ Chart.yaml | 6 + LICENSE | 202 +++++++++++++++++++++++++ Makefile | 48 ++++++ README.md | 64 ++++++++ README.md.gotmpl | 20 +++ templates/job-waitForMetalNode.yaml | 34 +++++ templates/rbac/role.yaml | 39 +++++ templates/rbac/rolebinding.yaml | 29 ++++ templates/rbac/serviceAccount.yaml | 9 ++ templates/virtual-machines.yaml | 176 +++++++++++++++++++++ values.yaml | 76 ++++++++++ 18 files changed, 855 insertions(+) create mode 100644 .github/linters/.checkov.yaml create mode 100644 .github/linters/.jscpd.json create mode 100644 .github/linters/.textlintrc create mode 100644 .github/workflows/helm-lint.yml create mode 100644 .github/workflows/helm-unittest.yml create mode 100644 .github/workflows/superlinter.yml create mode 100644 .github/workflows/update-helm-repo.yml create mode 100644 Chart.yaml create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 README.md.gotmpl create mode 100644 templates/job-waitForMetalNode.yaml create mode 100644 templates/rbac/role.yaml create mode 100644 templates/rbac/rolebinding.yaml create mode 100644 templates/rbac/serviceAccount.yaml create mode 100644 templates/virtual-machines.yaml create mode 100644 values.yaml diff --git a/.github/linters/.checkov.yaml b/.github/linters/.checkov.yaml new file mode 100644 index 0000000..fe4590f --- /dev/null +++ b/.github/linters/.checkov.yaml @@ -0,0 +1,12 @@ +--- +compact: true +directory: + - . +skip-path: + - tests +skip-check: + - CKV_K8S_49 # Minimize wildcard use in Roles and ClusterRoles + - CKV_K8S_155 # Minimize ClusterRoles that grant control over validating or mutating admission webhook configurations + - CKV_K8S_156 # Minimize ClusterRoles that grant permissions to approve CertificateSigningRequests + - CKV_K8S_157 # Minimize Roles and ClusterRoles that grant permissions to bind RoleBindings or ClusterRoleBindings + - CKV_K8S_158 # Minimize Roles and ClusterRoles that grant permissions to escalate Roles or ClusterRoles diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json new file mode 100644 index 0000000..f771907 --- /dev/null +++ b/.github/linters/.jscpd.json @@ -0,0 +1,10 @@ +{ + "ignore": [ + "templates/plumbing/applications.yaml", + "templates/imperative/_helpers.tpl", + "templates/core/subscriptions.yaml", + "templates/core/namespaces.yaml", + "templates/core/nodes.yaml", + "templates/_helpers.tpl" + ] +} diff --git a/.github/linters/.textlintrc b/.github/linters/.textlintrc new file mode 100644 index 0000000..7ab7e5f --- /dev/null +++ b/.github/linters/.textlintrc @@ -0,0 +1,10 @@ +{ + "rules": { + "terminology": { + // Excludes terms + "exclude": [ + "URL" + ] + } + } +} diff --git a/.github/workflows/helm-lint.yml b/.github/workflows/helm-lint.yml new file mode 100644 index 0000000..3377308 --- /dev/null +++ b/.github/workflows/helm-lint.yml @@ -0,0 +1,29 @@ +--- +name: Helm lint + +# +# Documentation: +# https://help.github.com/en/articles/workflow-syntax-for-github-actions +# + +permissions: read-all + +on: [push, pull_request] + +jobs: + build: + name: Run helm lint over the chart + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup helm + uses: azure/setup-helm@v4 + with: + version: 'v3.14.0' + + - name: Run make helmlint + run: | + make helm-lint diff --git a/.github/workflows/helm-unittest.yml b/.github/workflows/helm-unittest.yml new file mode 100644 index 0000000..9540437 --- /dev/null +++ b/.github/workflows/helm-unittest.yml @@ -0,0 +1,25 @@ +--- +name: Helm Unit Test + +# +# Documentation: +# https://help.github.com/en/articles/workflow-syntax-for-github-actions +# + +permissions: read-all + +on: [push, pull_request] + +jobs: + build: + name: Run helm lint over the chart + # It has to be 24.04 because -latest has a podman version that is too old + runs-on: ubuntu-24.04 + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Run make helmlint + run: | + make helm-unittest diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml new file mode 100644 index 0000000..8043a1c --- /dev/null +++ b/.github/workflows/superlinter.yml @@ -0,0 +1,36 @@ +--- +name: Super linter + +on: [push, pull_request] +permissions: read-all + +jobs: + build: + # Name the Job + name: Super linter + # Set the agent to run on + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + # Full git history is needed to get a proper list of changed files within `super-linter` + fetch-depth: 0 + + ################################ + # Run Linter against code base # + ################################ + - name: Lint Code Base + uses: super-linter/super-linter/slim@v7 + env: + VALIDATE_ALL_CODEBASE: true + DEFAULT_BRANCH: main + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # These are the validation we disable atm + VALIDATE_JSON_PRETTIER: false + VALIDATE_KUBERNETES_KUBECONFORM: false + VALIDATE_MARKDOWN: false + VALIDATE_MARKDOWN_PRETTIER: false + VALIDATE_YAML: false + VALIDATE_YAML_PRETTIER: false diff --git a/.github/workflows/update-helm-repo.yml b/.github/workflows/update-helm-repo.yml new file mode 100644 index 0000000..fa1d624 --- /dev/null +++ b/.github/workflows/update-helm-repo.yml @@ -0,0 +1,30 @@ +# This invokes the workflow named 'publish-charts' in the umbrella repo +# It expects to have a secret called CHARTS_REPOS_TOKEN which contains +# the GitHub token that has permissions to invoke workflows and commit code +# inside the umbrella-repo. +# The following fine-grained permissions were used in testing and were limited +# to the umbrella repo only: +# - Actions: r/w +# - Commit statuses: r/w +# - Contents: r/w +# - Deployments: r/w +# - Pages: r/w +# + +name: vp-patterns/update-helm-repo +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + helmlint: + uses: validatedpatterns/helm-charts/.github/workflows/helmlint.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 + permissions: + contents: read + + update-helm-repo: + needs: [helmlint] + uses: validatedpatterns/helm-charts/.github/workflows/update-helm-repo.yml@985ba37e0eb50b1b35ec194fc999eae2d0ae1486 + permissions: read-all + secrets: inherit diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 0000000..96f2bf3 --- /dev/null +++ b/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: edge-gitops-vms +description: Edge GitOps VMs +type: application +version: 0.1.0 +dependencies: [ ] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e2f3ffa --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +# https://hub.docker.com/r/helmunittest/helm-unittest/tags/ +HELM_UNITTEST_IMAGE ?= docker.io/helmunittest/helm-unittest:3.14.4-0.5.0 +HELM_DOCS_IMAGE ?= docker.io/jnorwood/helm-docs:latest + +PWD=$(shell pwd) +MYNAME=$(shell id -n -u) +MYUID=$(shell id -u) +MYGID=$(shell id -g) +PODMAN_ARGS := --security-opt label=disable --net=host --rm --passwd-entry "$(MYNAME):x:$(MYUID):$(MYGID)::/apps:/bin/bash" --user $(MYUID):$(MYGID) --userns keep-id:uid=$(MYUID),gid=$(MYGID) +##@ Common Tasks + +.PHONY: help +help: ## This help message + @echo "Pattern: $(NAME)" + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^(\s|[a-zA-Z_0-9-])+:.*?##/ { printf " \033[36m%-35s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +.PHONY: helm-lint +helm-lint: ## Runs helm lint against the chart + helm lint . + +.PHONY: helm-unittest +helm-unittest: ## Runs the helm unit tests + podman run $(PODMAN_ARGS) -v $(PWD):/apps:rw $(HELM_UNITTEST_IMAGE) . + +.PHONY: helm-docs +helm-docs: ## Generates README.md from values.yaml + # First make sure all values.yaml entries are documented. This can only be enabled once + # https://www.github.com/norwoodj/helm-docs/issues/228 is fixed + # podman run $(PODMAN_ARGS) -v $(PWD):/helm-docs:rw $(HELM_DOCS_IMAGE) -x + # Then render the README.md file + podman run $(PODMAN_ARGS) -v $(PWD):/helm-docs:rw $(HELM_DOCS_IMAGE) + +.PHONY: test +test: helm-lint helm-unittest ## Runs helm lint and unit tests + +.PHONY: super-linter +super-linter: ## Runs super linter locally + rm -rf .mypy_cache + podman run -e RUN_LOCAL=true -e USE_FIND_ALGORITHM=true \ + -e VALIDATE_JSON_PRETTIER=false \ + -e VALIDATE_KUBERNETES_KUBECONFORM=false \ + -e VALIDATE_MARKDOWN=false \ + -e VALIDATE_MARKDOWN_PRETTIER=false \ + -e VALIDATE_YAML_PRETTIER=false \ + -e VALIDATE_YAML=false \ + -v $(PWD):/tmp/lint:rw,z \ + -w /tmp/lint \ + ghcr.io/super-linter/super-linter:slim-v7 diff --git a/README.md b/README.md new file mode 100644 index 0000000..b69d980 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# edge-gitops-vms + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + +Edge GitOps VMs + +This chart is used to set up Edge GitOps VMs in conjunction with OpenShift Virtualization + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| global.pattern | string | `"ansible-edge-gitops"` | | +| job.image | string | `"image-registry.openshift-image-registry.svc:5000/openshift/cli:latest"` | | +| jobTerminationGracePeriod | int | `3600` | | +| rbac.roleBindings[0].createBinding | bool | `true` | | +| rbac.roleBindings[0].name | string | `"view-machine-api"` | | +| rbac.roleBindings[0].roleRef.kind | string | `"ClusterRole"` | | +| rbac.roleBindings[0].roleRef.name | string | `"view-machine-api"` | | +| rbac.roleBindings[0].scope.cluster | bool | `false` | | +| rbac.roleBindings[0].scope.namespace | string | `"openshift-machine-api"` | | +| rbac.roleBindings[0].subjects.apiGroup | string | `""` | | +| rbac.roleBindings[0].subjects.kind | string | `"ServiceAccount"` | | +| rbac.roleBindings[0].subjects.name | string | `"ansible-edge-gitops-sa"` | | +| rbac.roleBindings[0].subjects.namespace | string | `"edge-gitops-vms"` | | +| rbac.roles[0].apiGroups[0] | string | `"machine.openshift.io"` | | +| rbac.roles[0].createRole | bool | `true` | | +| rbac.roles[0].name | string | `"view-machine-api"` | | +| rbac.roles[0].resources[0] | string | `"machinesets"` | | +| rbac.roles[0].scope.cluster | bool | `true` | | +| rbac.roles[0].verbs[0] | string | `"get"` | | +| rbac.roles[0].verbs[1] | string | `"list"` | | +| rbac.roles[0].verbs[2] | string | `"watch"` | | +| secretStore.kind | string | `"ClusterSecretStore"` | | +| secretStore.name | string | `"vault-backend"` | | +| serviceAccountName | string | `"ansible-edge-gitops-sa"` | | +| vmDefaults.accessMode | string | `"ReadWriteMany"` | | +| vmDefaults.cloudInitSecret | string | `"secret/data/hub/cloud-init"` | | +| vmDefaults.cloudinitsecret | string | `"secret/data/hub/cloud-init"` | | +| vmDefaults.cores | int | `1` | | +| vmDefaults.count | int | `1` | | +| vmDefaults.flavor | string | `"medium"` | | +| vmDefaults.machineType | string | `"pc-q35-rhel8.4.0"` | | +| vmDefaults.memory | string | `"4Gi"` | | +| vmDefaults.os | string | `"rhel8"` | | +| vmDefaults.ports[0].name | string | `"ssh"` | | +| vmDefaults.ports[0].port | int | `22` | | +| vmDefaults.ports[0].protocol | string | `"TCP"` | | +| vmDefaults.ports[0].targetPort | int | `22` | | +| vmDefaults.sockets | int | `1` | | +| vmDefaults.sshpubkeyfield | string | `"publickey"` | | +| vmDefaults.sshsecret | string | `"secret/data/hub/vm-ssh"` | | +| vmDefaults.storage | string | `"30Gi"` | | +| vmDefaults.storageClassName | string | `"ocs-storagecluster-ceph-rbd-virtualization"` | | +| vmDefaults.template | string | `"rhel8-desktop-medium"` | | +| vmDefaults.threads | int | `1` | | +| vmDefaults.volumeMode | string | `"Block"` | | +| vmDefaults.workload | string | `"desktop"` | | +| vmNamespace | string | `"edge-gitops-vms"` | | +| vms | object | `{}` | | +| waitForMetalNode | bool | `true` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/README.md.gotmpl b/README.md.gotmpl new file mode 100644 index 0000000..0e13e30 --- /dev/null +++ b/README.md.gotmpl @@ -0,0 +1,20 @@ +{{ template "chart.header" . }} +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +This chart is used to set up Edge GitOps VMs in conjunction with OpenShift Virtualization + +{{ template "chart.homepageLine" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/templates/job-waitForMetalNode.yaml b/templates/job-waitForMetalNode.yaml new file mode 100644 index 0000000..c60c90f --- /dev/null +++ b/templates/job-waitForMetalNode.yaml @@ -0,0 +1,34 @@ +{{ if .Values.waitForMetalNode }} +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + argocd.argoproj.io/sync-wave: "1" + name: job-wait-for-metal-node + namespace: {{ .Values.vmNamespace }} +spec: + template: + spec: + containers: + - image: {{ .Values.job.image }} + command: + - /bin/bash + - -c + - | + while [ 1 ]; + do + nodes=$(oc get machineset -n openshift-machine-api -l 'edge-gitops-role=kubevirt-worker' -o jsonpath='{.items[*].status.availableReplicas}') + if [ "0$nodes" -ge "1" ]; then + echo "Node is ready, exiting" + exit 0 + fi + echo "Node is not yet ready, waiting" + sleep 15 + done + name: wait-for-metal-node + dnsPolicy: ClusterFirst + restartPolicy: Never + serviceAccount: {{ .Values.serviceAccountName }} + serviceAccountName: {{ .Values.serviceAccountName }} + terminationGracePeriodSeconds: {{ .Values.jobTerminationGracePeriod }} +{{ end }} diff --git a/templates/rbac/role.yaml b/templates/rbac/role.yaml new file mode 100644 index 0000000..54df29a --- /dev/null +++ b/templates/rbac/role.yaml @@ -0,0 +1,39 @@ +{{ if .Values.waitForMetalNode }} +{{- range $key, $value := .Values.rbac.roles }} +{{- if $value.createRole }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if $value.scope.cluster }} +kind: ClusterRole +metadata: + name: {{ .name }} +{{- else }} +kind: Role +metadata: + name: {{ $value.name }} + namespace: {{ $value.scope.namespace}} +{{- end }} + annotations: + argocd.argoproj.io/sync-wave: "1" +rules: + - apiGroups: +{{- range $value.apiGroups }} + - {{ . }} +{{- end }} + resources: +{{- range $value.resources }} + - {{ . }} +{{- end }} +{{- if (($value.resourceNames)) }} + resourceNames: +{{- range $value.resourceNames }} + - {{ . }} +{{- end }} +{{- end }} + verbs: +{{- range $value.verbs }} + - {{ . }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/rbac/rolebinding.yaml b/templates/rbac/rolebinding.yaml new file mode 100644 index 0000000..825d08d --- /dev/null +++ b/templates/rbac/rolebinding.yaml @@ -0,0 +1,29 @@ +{{ if .Values.waitForMetalNode }} +{{- range $key, $value := .Values.rbac.roleBindings }} +{{- if $value.createBinding }} +apiVersion: rbac.authorization.k8s.io/v1 +{{- if $value.scope.cluster }} +kind: ClusterRoleBinding +{{- else }} +kind: RoleBinding +{{- end }} +metadata: + name: {{ .name }} +{{- if eq $value.scope.cluster false }} + namespace: {{ $value.scope.namespace }} +{{- end }} + annotations: + argocd.argoproj.io/sync-wave: "1" +subjects: +- kind: {{ $value.subjects.kind | default "ServiceAccount" }} + name: {{ $value.subjects.name }} + namespace: {{ $value.subjects.namespace | default $.Values.global.pattern }} + apiGroup: "" +roleRef: + kind: {{ $value.roleRef.kind }} + name: {{ $value.roleRef.name }} + apiGroup: rbac.authorization.k8s.io +--- +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/rbac/serviceAccount.yaml b/templates/rbac/serviceAccount.yaml new file mode 100644 index 0000000..2b464a6 --- /dev/null +++ b/templates/rbac/serviceAccount.yaml @@ -0,0 +1,9 @@ +{{ if .Values.waitForMetalNode }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.serviceAccountName }} + namespace: {{ .Values.vms.namespace }} + annotations: + argocd.argoproj.io/sync-wave: "1" +{{ end }} diff --git a/templates/virtual-machines.yaml b/templates/virtual-machines.yaml new file mode 100644 index 0000000..2d4b482 --- /dev/null +++ b/templates/virtual-machines.yaml @@ -0,0 +1,176 @@ +{{- $def := .Values.vmDefaults }} +{{- range $vm, $vmr := .Values.vms }} +--- +apiVersion: "external-secrets.io/v1beta1" +kind: ExternalSecret +metadata: + name: es-cloudinit-{{ $vmr.role }} + annotations: + argocd.argoproj.io/sync-wave: "2" +spec: + refreshInterval: 90s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: cloudinit-{{ $vmr.role }} + template: + dataFrom: + - extract: + key: {{ coalesce $vmr.cloudinitsecret $def.cloudinitsecret }} +{{- $ctr := int (coalesce $vmr.count $def.count) }} +{{- range $i := until $ctr }} +{{- $idx := printf "%03d" (add $i 1) }} +{{- $identifier := printf "%s-%s-%s" (coalesce $vmr.os $def.os) $vmr.role $idx }} +--- +apiVersion: "external-secrets.io/v1beta1" +kind: ExternalSecret +metadata: + name: es-authorizedsshkeys-{{ $identifier }} + annotations: + argocd.argoproj.io/sync-wave: "2" +spec: + refreshInterval: 90s + secretStoreRef: + name: {{ $.Values.secretStore.name }} + kind: {{ $.Values.secretStore.kind }} + target: + name: authorizedsshkeys-{{ $identifier }} + template: + data: + - secretKey: key + remoteRef: + key: {{ coalesce $vmr.sshsecret $def.sshsecret }} + property: {{ coalesce $vmr.sshpubkeyfield $def.sshpubkeyfield }} +--- +apiVersion: v1 +items: +- apiVersion: kubevirt.io/v1 + kind: VirtualMachine + metadata: + annotations: + vm.kubevirt.io/validations: | + [ + { + "name": "minimal-required-memory", + "path": "jsonpath::.spec.domain.resources.requests.memory", + "rule": "integer", + "message": "This VM requires more memory.", + "min": 1610612736 + } + ] + argocd.argoproj.io/sync-wave: "2" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + {{- range $ann_k, $ann_v := (coalesce $vmr.extraAnnotations $def.extraAnnotations nil) }} + {{ $ann_k }}: {{ $ann_v }} + {{- end }} + labels: + app: {{ $identifier }} + edge-gitops-role: {{ $vmr.role }} + vm.kubevirt.io/template: {{ coalesce $vmr.template $def.template }} + vm.kubevirt.io/template.revision: "1" + vm.kubevirt.io/template.version: v0.19.4 + {{- range $lbl_k, $lbl_v := (coalesce $vmr.extraLabels $def.extraLabels nil) }} + {{ $lbl_k }}: {{ $lbl_v }} + {{- end }} + name: {{ $identifier }} + spec: + dataVolumeTemplates: + - apiVersion: cdi.kubevirt.io/v1beta1 + kind: DataVolume + metadata: + name: {{ $identifier }} + spec: + sourceRef: + kind: DataSource + name: {{ coalesce $vmr.dataSource $def.dataSource $vmr.os $def.os }} + namespace: openshift-virtualization-os-images + pvc: + accessModes: + - {{ coalesce $vmr.accessMode $def.accessMode }} + resources: + requests: + storage: {{ coalesce $vmr.storage $def.storage }} + storageClassName: {{ coalesce $vmr.storageClassName $def.storageClassName }} + volumeMode: {{ coalesce $vmr.volumeMode $def.volumeMode }} + running: true + template: + metadata: + annotations: + vm.kubevirt.io/flavor: {{ coalesce $vmr.flavor $def.flavor }} + vm.kubevirt.io/os: {{ coalesce $vmr.os $def.os }} + vm.kubevirt.io/workload: {{ coalesce $vmr.workload $def.workload }} + labels: + kubevirt.io/domain: {{ $identifier }} + kubevirt.io/size: {{ coalesce $vmr.flavor $def.flavor }} + vm.kubevirt.io/name: {{ $identifier }} + spec: + accessCredentials: + - sshPublicKey: + propagationMethod: + configDrive: {} + source: + secret: + secretName: authorizedsshkeys-{{ $identifier }} + domain: + cpu: + cores: {{ coalesce $vmr.cores $def.cores }} + sockets: {{ coalesce $vmr.sockets $def.sockets }} + threads: {{ coalesce $vmr.threads $def.threads }} + devices: + disks: + - disk: + bus: virtio + name: {{ $identifier }} + - disk: + bus: virtio + name: cloudinitdisk + inputs: + - bus: virtio + name: tablet + type: tablet + interfaces: + - masquerade: {} + name: default + networkInterfaceMultiqueue: true + rng: {} + machine: + type: {{ coalesce $vmr.machineType $def.machineType }} + resources: + requests: + memory: {{ coalesce $vmr.memory $def.memory }} + evictionStrategy: LiveMigrate + networks: + - name: default + pod: {} + terminationGracePeriodSeconds: 180 + volumes: + - dataVolume: + name: {{ $identifier }} + name: {{ $identifier }} + - name: cloudinitdisk + cloudInitConfigDrive: + secretRef: + name: cloudinit-{{ $vmr.role }} +- apiVersion: v1 + kind: Service + metadata: + name: {{ $identifier }} + annotations: + argocd.argoproj.io/sync-wave: "2" + labels: + app: {{ $identifier }} + edge-gitops-role: {{ $vmr.role }} + spec: + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: {{ (coalesce $vmr.ports $def.ports) | toPrettyJson }} + selector: + vm.kubevirt.io/name: {{ $identifier }} + sessionAffinity: None + type: NodePort +kind: List +metadata: {} +{{- end }} +{{- end }} diff --git a/values.yaml b/values.yaml new file mode 100644 index 0000000..d8f7fb6 --- /dev/null +++ b/values.yaml @@ -0,0 +1,76 @@ +--- +global: + pattern: ansible-edge-gitops + +secretStore: + name: vault-backend + kind: ClusterSecretStore + +vmNamespace: edge-gitops-vms + +waitForMetalNode: true +jobTerminationGracePeriod: 3600 + +vmDefaults: + cloudInitSecret: secret/data/hub/cloud-init + accessMode: "ReadWriteMany" + storageClassName: "ocs-storagecluster-ceph-rbd-virtualization" + volumeMode: "Block" + count: 1 + flavor: medium + workload: desktop + os: rhel8 + storage: 30Gi + memory: 4Gi + machineType: pc-q35-rhel8.4.0 + cores: 1 + sockets: 1 + threads: 1 + template: rhel8-desktop-medium + sshsecret: secret/data/hub/vm-ssh + cloudinitsecret: secret/data/hub/cloud-init + sshpubkeyfield: publickey + # extraLabels is available for default extra labels to add to the virtualmachine + # extraAnnotations is available for default extra labels to add to the virtualmachine + ports: + - name: ssh + port: 22 + protocol: TCP + targetPort: 22 + +# Define the VMs you want to create with any specific attributes from vmDefaults +# in an overrides file. +vms: {} + +serviceAccountName: ansible-edge-gitops-sa +rbac: + roles: + - name: view-machine-api + createRole: true + apiGroups: + - machine.openshift.io + scope: + cluster: true + resources: + - machinesets + verbs: + - "get" + - "list" + - "watch" + roleBindings: + - name: view-machine-api + createBinding: true + scope: + cluster: false + namespace: "openshift-machine-api" + subjects: + kind: ServiceAccount + name: ansible-edge-gitops-sa + namespace: edge-gitops-vms + apiGroup: "" + roleRef: + kind: ClusterRole + name: view-machine-api + +job: + image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest