From fd7043b0191329ff494bb554c3a1ede29921b1de Mon Sep 17 00:00:00 2001 From: Elie CHARRA Date: Wed, 21 Feb 2024 11:24:22 +0100 Subject: [PATCH] Initial commit --- .github/workflows/build.yml | 32 ++ README.md | 84 ++++ app/Dockerfile | 12 + app/go.mod | 3 + app/main.go | 32 ++ controller/manifests.yaml | 725 +++++++++++++++++++++++++++ infra/helm/.helmignore | 23 + infra/helm/Chart.yaml | 24 + infra/helm/templates/_helpers.tpl | 47 ++ infra/helm/templates/deployment.yaml | 32 ++ infra/helm/templates/service.yaml | 15 + infra/helm/values.yaml | 18 + infra/spacelift/run.yaml | 10 + infra/spacelift/stack.yaml | 27 + infra/tf/.terraform.lock.hcl | 36 ++ infra/tf/main.tf | 3 + infra/tf/provider.tf | 3 + infra/tf/s3.tf | 10 + 18 files changed, 1136 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 README.md create mode 100644 app/Dockerfile create mode 100644 app/go.mod create mode 100644 app/main.go create mode 100644 controller/manifests.yaml create mode 100644 infra/helm/.helmignore create mode 100644 infra/helm/Chart.yaml create mode 100644 infra/helm/templates/_helpers.tpl create mode 100644 infra/helm/templates/deployment.yaml create mode 100644 infra/helm/templates/service.yaml create mode 100644 infra/helm/values.yaml create mode 100644 infra/spacelift/run.yaml create mode 100644 infra/spacelift/stack.yaml create mode 100644 infra/tf/.terraform.lock.hcl create mode 100644 infra/tf/main.tf create mode 100644 infra/tf/provider.tf create mode 100644 infra/tf/s3.tf diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..1333985 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,32 @@ +name: Build docker image +on: + push: + branches: + - main +permissions: + contents: read + packages: write +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: ./app + platforms: linux/amd64,linux/arm64 + push: true + tags: | + ghcr.io/spacelift-io/spacelift-operator-demo:latest + ghcr.io/spacelift-io/spacelift-operator-demo:${{ github.sha }} diff --git a/README.md b/README.md new file mode 100644 index 0000000..8be70f1 --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +# Setup + +## (optional) Configure org + +You can skip this step (recommended) if you want to simply run the demo against the preconfigured preprod env. + +- Create a space to run this demo +- Make sure the VCS integration is configured so this repo could be reached +- Create a push policy to ignore all VCS events with label `autoattach:argo` + +```rego +package spacelift + +ignore { + true +} +``` + +- Create an api key in your org, and allow access to the space + +```rego +package spacelift + +key := "api::CHANGEME" +space_id = "spacelift-operator-CHANGEME" + +space_admin[space_id] { + input.session.login == key +} +allow {input.session.login == key} +write {input.session.login == key} +sample { true } +``` + +## Install the operator + +You can jump directly to this step and ask for a valid token. + +Create a secret in your cluster to allow the controller to perform changes on spacelift backend. +It's simpler to configure a token the dedicated space on spacelift preprod because everything is configured. +The secret should be created in the same namespace as Stack and Run resources that we are going to create afterward. + +```shell +kubectl create secret generic spacelift-credentials\ + --from-literal=SPACELIFT_API_KEY_ENDPOINT='https://spacelift-io.app.spacelift.dev'\ + --from-literal=SPACELIFT_API_KEY_ID='CHANGEME'\ + --from-literal=SPACELIFT_API_KEY_SECRET='CHANGEME' +``` + +Install the operator with the following command + +```shell +kubectl apply -f controller +``` + +# Deployment + +## Helm + +```shell +# Create a stack +kubectl apply -f infra/spacelift/stack.yaml &&\ +kubectl wait --for=jsonpath='{.status.ready}'=true stack/demo-stack --timeout 1h + +# Trigger a run +kubectl delete --ignore-not-found=true -f infra/spacelift/run.yaml &&\ +kubectl apply -f infra/spacelift/run.yaml &&\ +kubectl wait --for=jsonpath='{.status.ready}'=true run/spacelift-operator-demo --timeout 1h + +# Deploy the app +helm upgrade --install operator-demo ./infra/helm/ --set 'image.tag=ed35e9a152f61eb79d369cd9b2dd7e4f65629026' +``` + +Now you can connect to the app and see that the bucket URL has been injected as env var + +```shell +kubectl port-forward service/operator-demo 8888 +``` + +And then open http://localhost:8888 + +## Argo + +TODO diff --git a/app/Dockerfile b/app/Dockerfile new file mode 100644 index 0000000..20c2dbb --- /dev/null +++ b/app/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.21.7 as builder +WORKDIR /build +COPY . . +RUN GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o demo . + +FROM scratch +USER 1000 +COPY --from=builder /build/demo /bin/demo +ENTRYPOINT ["/bin/demo"] + + + diff --git a/app/go.mod b/app/go.mod new file mode 100644 index 0000000..7ffef20 --- /dev/null +++ b/app/go.mod @@ -0,0 +1,3 @@ +module github.com/spacelift-io/spacelift-operator-demo + +go 1.21.7 diff --git a/app/main.go b/app/main.go new file mode 100644 index 0000000..5bca7d0 --- /dev/null +++ b/app/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "errors" + "fmt" + "log" + "net/http" + "os" +) + +func main() { + listenAddr := ":8888" + http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { + log.Println(request.Method, request.URL.String()) + if request.URL.Path != "/" { + writer.WriteHeader(http.StatusNotFound) + return + } + secrets := os.Environ() + body := "" + for i := 0; i < len(secrets); i++ { + body += fmt.Sprintf("%s\n", secrets[i]) + } + _, _ = writer.Write([]byte(body)) + }) + log.Printf("Listening on %s\n", listenAddr) + if err := http.ListenAndServe(listenAddr, nil); err != nil { + if !errors.Is(err, http.ErrServerClosed) { + log.Fatal(err) + } + } +} diff --git a/controller/manifests.yaml b/controller/manifests.yaml new file mode 100644 index 0000000..aca725b --- /dev/null +++ b/controller/manifests.yaml @@ -0,0 +1,725 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: spacelift-operator + app.kubernetes.io/instance: system + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: namespace + app.kubernetes.io/part-of: spacelift-operator + control-plane: controller-manager + name: spacelift-operator-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: runs.app.spacelift.io +spec: + group: app.spacelift.io + names: + kind: Run + listKind: RunList + plural: runs + singular: run + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.state + name: State + type: string + - jsonPath: .status.id + name: Id + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: Run is the Schema for the runs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: RunSpec defines the desired state of Run + properties: + stackName: + description: StackName is the name of the stack for this run, this + is mandatory + minLength: 1 + type: string + required: + - stackName + type: object + status: + description: RunStatus defines the observed state of Run + properties: + argo: + description: Argo is a status that could be used by argo health check + to sync on health + properties: + health: + description: ArgoHealth is a string type to represent the argo + health of a resource More info on the argo doc here https://argo-cd.readthedocs.io/en/stable/operator-manual/health/ + type: string + required: + - health + type: object + id: + description: Id is the run ULID on Spacelift + type: string + ready: + type: boolean + stackId: + type: string + state: + description: State is the run state, see RunState for all possibles + state of a run + type: string + required: + - ready + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: stacks.app.spacelift.io +spec: + group: app.spacelift.io + names: + kind: Stack + listKind: StackList + plural: stacks + singular: stack + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: Stack is the Schema for the stacks API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: StackSpec defines the desired state of Stack + properties: + commitSHA: + minLength: 1 + type: string + name: + type: string + settings: + properties: + additionalProjectGlobs: + items: + type: string + type: array + administrative: + type: boolean + afterApply: + items: + type: string + type: array + afterDestroy: + items: + type: string + type: array + afterInit: + items: + type: string + type: array + afterPerform: + items: + type: string + type: array + afterPlan: + items: + type: string + type: array + afterRun: + items: + type: string + type: array + autodeploy: + type: boolean + autoretry: + type: boolean + awsIntegration: + properties: + id: + type: string + read: + type: boolean + write: + type: boolean + required: + - id + - read + - write + type: object + beforeApply: + items: + type: string + type: array + beforeDestroy: + items: + type: string + type: array + beforeInit: + items: + type: string + type: array + beforePerform: + items: + type: string + type: array + beforePlan: + items: + type: string + type: array + branch: + type: string + description: + type: string + githubActionDeploy: + type: boolean + isDisabled: + type: boolean + labels: + items: + type: string + type: array + localPreviewEnabled: + type: boolean + managesStateFile: + description: In our API managesStateFile is not part of StackInput + type: boolean + namespace: + type: string + projectRoot: + type: string + protectFromDeletion: + type: boolean + provider: + type: string + repository: + type: string + repositoryURL: + type: string + runnerImage: + type: string + space: + type: string + terraformVersion: + type: string + vcsIntegrationId: + type: string + vendorConfig: + properties: + ansible: + properties: + playbook: + type: string + required: + - playbook + type: object + cloudFormation: + properties: + entryTemplateFile: + type: string + region: + type: string + stackName: + type: string + templateBucket: + type: string + required: + - entryTemplateFile + - region + - stackName + - templateBucket + type: object + kubernetes: + properties: + kubectlVersion: + type: string + namespace: + type: string + required: + - namespace + type: object + pulumi: + properties: + loginURL: + type: string + stackName: + type: string + required: + - loginURL + - stackName + type: object + terraform: + properties: + externalStateAccessEnabled: + type: boolean + useSmartSanitization: + type: boolean + version: + type: string + workflowTool: + type: string + workspace: + type: string + type: object + terragrunt: + properties: + terraformVersion: + type: string + terragruntVersion: + type: string + useRunAll: + type: boolean + useSmartSanitization: + type: boolean + required: + - terraformVersion + - terragruntVersion + - useRunAll + - useSmartSanitization + type: object + type: object + workerPool: + type: string + required: + - administrative + - branch + - repository + type: object + required: + - commitSHA + - name + - settings + type: object + status: + description: StackStatus defines the observed state of Stack + properties: + id: + description: State is the stack state + type: string + ready: + type: boolean + trackedCommit: + properties: + authorLogin: + type: string + authorName: + type: string + hash: + type: string + message: + type: string + timestamp: + type: integer + url: + type: string + required: + - authorName + - hash + - message + - timestamp + type: object + trackedCommitSetBy: + type: string + url: + type: string + required: + - ready + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: spacelift-operator + app.kubernetes.io/instance: controller-manager-sa + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: serviceaccount + app.kubernetes.io/part-of: spacelift-operator + name: spacelift-operator-controller-manager + namespace: spacelift-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: spacelift-operator + app.kubernetes.io/instance: leader-election-role + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: role + app.kubernetes.io/part-of: spacelift-operator + name: spacelift-operator-leader-election-role + namespace: spacelift-operator-system +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: spacelift-operator-manager-role +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - app.spacelift.io + resources: + - runs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - app.spacelift.io + resources: + - runs/finalizers + verbs: + - update +- apiGroups: + - app.spacelift.io + resources: + - runs/status + verbs: + - get + - patch + - update +- apiGroups: + - app.spacelift.io + resources: + - stacks + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - app.spacelift.io + resources: + - stacks/finalizers + verbs: + - update +- apiGroups: + - app.spacelift.io + resources: + - stacks/status + verbs: + - get + - patch + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: spacelift-operator + app.kubernetes.io/instance: metrics-reader + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: clusterrole + app.kubernetes.io/part-of: spacelift-operator + name: spacelift-operator-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: spacelift-operator + app.kubernetes.io/instance: proxy-role + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: clusterrole + app.kubernetes.io/part-of: spacelift-operator + name: spacelift-operator-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: spacelift-operator + app.kubernetes.io/instance: leader-election-rolebinding + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: rolebinding + app.kubernetes.io/part-of: spacelift-operator + name: spacelift-operator-leader-election-rolebinding + namespace: spacelift-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: spacelift-operator-leader-election-role +subjects: +- kind: ServiceAccount + name: spacelift-operator-controller-manager + namespace: spacelift-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: spacelift-operator + app.kubernetes.io/instance: manager-rolebinding + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/part-of: spacelift-operator + name: spacelift-operator-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: spacelift-operator-manager-role +subjects: +- kind: ServiceAccount + name: spacelift-operator-controller-manager + namespace: spacelift-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: spacelift-operator + app.kubernetes.io/instance: proxy-rolebinding + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/part-of: spacelift-operator + name: spacelift-operator-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: spacelift-operator-proxy-role +subjects: +- kind: ServiceAccount + name: spacelift-operator-controller-manager + namespace: spacelift-operator-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: spacelift-operator + app.kubernetes.io/instance: controller-manager-metrics-service + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: service + app.kubernetes.io/part-of: spacelift-operator + control-plane: controller-manager + name: spacelift-operator-controller-manager-metrics-service + namespace: spacelift-operator-system +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: https + selector: + control-plane: controller-manager +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: spacelift-operator + app.kubernetes.io/instance: controller-manager + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: deployment + app.kubernetes.io/part-of: spacelift-operator + control-plane: controller-manager + name: spacelift-operator-controller-manager + namespace: spacelift-operator-system +spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + control-plane: controller-manager + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: + - amd64 + - arm64 + - key: kubernetes.io/os + operator: In + values: + - linux + containers: + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.14.1 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /spacelift-operator + image: ghcr.io/spacelift-io/spacelift-operator:7434b15ea0f1cbd3a1cfe040f77b3ed4f9c8ab5d + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + serviceAccountName: spacelift-operator-controller-manager + terminationGracePeriodSeconds: 10 diff --git a/infra/helm/.helmignore b/infra/helm/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/infra/helm/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/infra/helm/Chart.yaml b/infra/helm/Chart.yaml new file mode 100644 index 0000000..f422eea --- /dev/null +++ b/infra/helm/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: operator-demo +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.0.0" diff --git a/infra/helm/templates/_helpers.tpl b/infra/helm/templates/_helpers.tpl new file mode 100644 index 0000000..a6b2985 --- /dev/null +++ b/infra/helm/templates/_helpers.tpl @@ -0,0 +1,47 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "operator-demo.name" -}} +{{- default .Chart.Name | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "operator-demo.fullname" -}} +{{- $name := default .Chart.Name }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "operator-demo.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "operator-demo.labels" -}} +helm.sh/chart: {{ include "operator-demo.chart" . }} +{{ include "operator-demo.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "operator-demo.selectorLabels" -}} +app.kubernetes.io/name: {{ include "operator-demo.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/infra/helm/templates/deployment.yaml b/infra/helm/templates/deployment.yaml new file mode 100644 index 0000000..8cb7163 --- /dev/null +++ b/infra/helm/templates/deployment.yaml @@ -0,0 +1,32 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "operator-demo.fullname" . }} + labels: + {{- include "operator-demo.labels" . | nindent 4 }} +spec: + replicas: 1 + selector: + matchLabels: + {{- include "operator-demo.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "operator-demo.labels" . | nindent 8 }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + envFrom: + - secretRef: + optional: false + name: {{ .Values.app.envFromSecret }} diff --git a/infra/helm/templates/service.yaml b/infra/helm/templates/service.yaml new file mode 100644 index 0000000..047bf56 --- /dev/null +++ b/infra/helm/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "operator-demo.fullname" . }} + labels: + {{- include "operator-demo.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "operator-demo.selectorLabels" . | nindent 4 }} diff --git a/infra/helm/values.yaml b/infra/helm/values.yaml new file mode 100644 index 0000000..5c7957c --- /dev/null +++ b/infra/helm/values.yaml @@ -0,0 +1,18 @@ +replicaCount: 1 +image: + repository: ghcr.io/spacelift-io/spacelift-operator-demo + pullPolicy: IfNotPresent + tag: "" +service: + type: ClusterIP + port: 8888 +livenessProbe: + httpGet: + path: / + port: http +readinessProbe: + httpGet: + path: / + port: http +app: + envFromSecret: stack-output-spacelift-operator-demo diff --git a/infra/spacelift/run.yaml b/infra/spacelift/run.yaml new file mode 100644 index 0000000..5312f2f --- /dev/null +++ b/infra/spacelift/run.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: app.spacelift.io/v1beta1 +kind: Run +metadata: + name: spacelift-operator-demo + annotations: + argocd.argoproj.io/hook: Sync + argocd.argoproj.io/sync-wave: "-1" +spec: + stackName: demo-stack diff --git a/infra/spacelift/stack.yaml b/infra/spacelift/stack.yaml new file mode 100644 index 0000000..1c25024 --- /dev/null +++ b/infra/spacelift/stack.yaml @@ -0,0 +1,27 @@ +apiVersion: app.spacelift.io/v1beta1 +kind: Stack +metadata: + name: demo-stack + annotations: + argocd.argoproj.io/sync-wave: "-2" +spec: + name: spacelift-operator-demo + commitSHA: 58de3fcc53659909a9779d3a5ed71aef1959b5a8 + settings: + administrative: false + space: spacelift-operator-01HR9KQ590MFT6H6ETFJ657KR6 + namespace: spacelift-io + repository: spacelift-operator-demo + projectRoot: infra/tf + branch: main + managesStateFile: true + awsIntegration: + id: 01HRQ4YMJQTP5R6K8Q9N1NB3XM + read: true + write: true + vendorConfig: + terraform: + version: 1.6.2 + workflowTool: OPEN_TOFU + labels: + - argo diff --git a/infra/tf/.terraform.lock.hcl b/infra/tf/.terraform.lock.hcl new file mode 100644 index 0000000..57b15de --- /dev/null +++ b/infra/tf/.terraform.lock.hcl @@ -0,0 +1,36 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/hashicorp/aws" { + version = "5.40.0" + hashes = [ + "h1:Z5VaTP8jwXG60IfHLaUIT2T+W0XvKrPWpZyxVmUUdsw=", + "zh:1689604abc131df74ef66839273474ee69a79256655de726242819a19c8d9f40", + "zh:1777da5659cd5a85d078674a7a80239f37397912036cb98e695ef5097d5ff53c", + "zh:216a63569d03e8fd1785dce59ceab182c84cf60dec6080e52e4d8e4e8be10cf3", + "zh:29ef1afbc819c5f52818f76ec1232f424a1502294774e32405c6c5e81e189331", + "zh:4bd44590ff060fac4b776c276f914a0d2225e78ec8a1cf83ea4236b0b29c7cbb", + "zh:75cceb4d9fa1736bd3969c7df2d477272f2187ff2f321fb2240a76e99b0e1f30", + "zh:c8938042376000ee904abddb8756533f2f4d861313cd4b99833df860ff0ed5f7", + "zh:eafe59e7fd68fb8d814a81c6139bd9463874e85f252ffefc9eb696c2e4828de6", + "zh:eef0ed78b52050afdd07563fcb2a0a49c9651edaf45f5810be71d60b141bb13c", + "zh:ff169ae274cd3cde075a9d3bfc2c6555d6ab40d6f9ced73ef14d31ef4ee58779", + ] +} + +provider "registry.opentofu.org/hashicorp/random" { + version = "3.6.0" + hashes = [ + "h1:6QMZ6JACl+V2t8daN5GTlw22EtG7nhc3BbkbJDw2a5M=", + "zh:486a1c921eab5c51a480f2eb0ad85173f207c9b7bb215f3893e58bc38d3b7c75", + "zh:6901b3afa4607d1e31934ba91ed2625215ada42b3518c3a9adeeac7a5f656dc3", + "zh:7e93752c9de710e417191353ad1a41b5a60432ab7ef4f8b556bf248297ec5e23", + "zh:c795d3d319e8ee7be972746b935963b7e772a6a14080261a35c03915c1f9ccb2", + "zh:cd4f8bcaf332828d1736c73874549c25e427737f136173c7b61e2df3db50e5d9", + "zh:e0103eb2e280989c3d9ffda5d6b413e8f583be21bc1d5754c6e9ca87ecc1c44a", + "zh:f4fbec2510322d5b7ad584a92436b5dbd0f2e897a3ec538932af59e245a4c8e4", + "zh:f5418842afd4aa7676e2456e425e8f573cb2b9bffd29bd7de09d91845644ab24", + "zh:f572a26f93d00ec42461ce478678366e570fa4497e2273f9d47f24cdfc4b42b4", + "zh:ff1f07c561a3f7f219b6fee1647a559933b5dd6181753e164c3978fd47a11685", + ] +} diff --git a/infra/tf/main.tf b/infra/tf/main.tf new file mode 100644 index 0000000..fb236b6 --- /dev/null +++ b/infra/tf/main.tf @@ -0,0 +1,3 @@ +output "BUCKET_URL" { + value = "https://${aws_s3_bucket.bucket.bucket_regional_domain_name}" +} diff --git a/infra/tf/provider.tf b/infra/tf/provider.tf new file mode 100644 index 0000000..e62fc36 --- /dev/null +++ b/infra/tf/provider.tf @@ -0,0 +1,3 @@ +provider "aws" { + region = "eu-west-1" +} diff --git a/infra/tf/s3.tf b/infra/tf/s3.tf new file mode 100644 index 0000000..ddfc3e4 --- /dev/null +++ b/infra/tf/s3.tf @@ -0,0 +1,10 @@ +resource "random_string" "random" { + length = 6 + special = false + numeric = false + upper = false +} + +resource "aws_s3_bucket" "bucket" { + bucket = "bucket-${random_string.random.result}" +}