From df150ea13da2c77bcde58ddca3e72c75ba40a599 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Mon, 4 Dec 2023 14:33:26 -0500 Subject: [PATCH] temp: devspace files --- charts/CHANGELOG | 188 +++++++++++++++ charts/Chart.lock | 6 + charts/Chart.yaml | 3 + charts/charts/django-ida-0.8.16.tgz | Bin 0 -> 7545 bytes charts/requirements.lock | 0 charts/requirements.yaml | 0 charts/templates/app-deployment.yaml | 218 ++++++++++++++++++ charts/templates/app-hpa.yaml | 24 ++ charts/templates/app-ingress.yaml | 68 ++++++ charts/templates/app-sa.yaml | 9 + charts/templates/app-service.yaml | 18 ++ .../templates/collectstatic-deployment.yaml | 194 ++++++++++++++++ charts/templates/collectstatic-sa.yaml | 9 + charts/templates/configmap.yaml | 47 ++++ charts/templates/cronjobs.yaml | 121 ++++++++++ charts/templates/workers.yaml | 158 +++++++++++++ charts/values.yaml | 130 +++++++++++ devspace.yaml | 98 ++++++++ devspace_start.sh | 36 +++ 19 files changed, 1327 insertions(+) create mode 100644 charts/CHANGELOG create mode 100644 charts/Chart.lock create mode 100644 charts/Chart.yaml create mode 100644 charts/charts/django-ida-0.8.16.tgz create mode 100644 charts/requirements.lock create mode 100644 charts/requirements.yaml create mode 100644 charts/templates/app-deployment.yaml create mode 100644 charts/templates/app-hpa.yaml create mode 100644 charts/templates/app-ingress.yaml create mode 100644 charts/templates/app-sa.yaml create mode 100644 charts/templates/app-service.yaml create mode 100644 charts/templates/collectstatic-deployment.yaml create mode 100644 charts/templates/collectstatic-sa.yaml create mode 100644 charts/templates/configmap.yaml create mode 100644 charts/templates/cronjobs.yaml create mode 100644 charts/templates/workers.yaml create mode 100644 charts/values.yaml create mode 100644 devspace.yaml create mode 100755 devspace_start.sh diff --git a/charts/CHANGELOG b/charts/CHANGELOG new file mode 100644 index 000000000..4714a3d16 --- /dev/null +++ b/charts/CHANGELOG @@ -0,0 +1,188 @@ +0.8.18 +Fix ingress configuration snippet so that it is not bound by allow_django_admin + +0.8.17 +Feature to add NewRelic HTTP header and client-max-body-size to ingress + +0.8.16 +Updated the User ID use in k8s-cli-utils. It was updated in this PR (https://github.com/edx/k8s-cli-utils/pull/14) + +0.8.15 +Switch k8s-cli-utils image from dockerhub to ECR + +0.8.14 +Feature to allow configurable resource limits for CronJobs + +0.8.13 +Add feature to allow custom migration commands + +0.8.12 +Fix feature to allow worker resource limits to be configurable + +0.8.11 +Properly quote and escape shell script interpolations for deployments and cron jobs as well. + +0.8.10 +Add feature to allow worker resource limits to be configurable + +0.8.9 +Fix app deployment issue by adding the missing mounted volumes section when migrations are disabled + +0.8.8 +Proceed with running Django migrations only when it is enabled + +0.8.7 +Added additionalLabels env, owner and team for kubecost aggregations using range + +0.8.6 +Increase memory limit for celery workers to fix discovery celery workers from crashing due to resource limits being exceeded + +0.8.5 +Fix YAML injection prevention; `quote` output's escaping is only compatible with YAML, not Bash strings + +0.8.4 +Prevented YAML injection in worker command + +0.8.3 +Updated the Cron job template to support schedules that need to be quoted. + +0.8.2 +Updated the Cron job Api version to batch/v1. Previous api version was deprecated in k8s 1.21 and removed in 1.25. + +0.8.1 +Add option to configure the name of python (e.g. python, python3, python3.9) used to run command in migration init +container. Defaults to python3. + +0.8.0 +Removed ingress class variable in favor of new className variable. The class variable is used to set the old annotation +kubernetes.io/ingress.class which has been replaced by ingressClassName in the spec of the v1 of the ingress api + +0.7.2 +Add extra_tls_hosts variable to allow adding extra hostnames to TLS certs +Make ingress and tls secret names stable on ingresses using the new className variable to prevent shuffling. + +0.7.1 +Add new ingress class variable to fix issue with 0.7.0 that causes new ingresses to not be created, because kubernetes +doesn't allow you to set the new className variable on the spec and the old annotation at the same time. + +0.7.0 +Add ingressClassName to ingress spec for compatibility with networking.k8s.io/v1 Ingress and Kubernetes 1.22 + +0.6.2 +Add custom annotation to ingress object for external dns cloudflare + +0.6.1 +Adding option to specify command for collectstatic job + +0.6.0 +Changed imagePullPolicy to Always for app containers to fix issue with images not updating after rebuild. Currently +image tags are not immutable, so we need to always check the Docker image repository for updates images. + +0.5.8 +Add custom annotation for external dns + +0.5.7 +Add health_check.host_header to customize livenessProbe HTTP Host header + +0.5.6 +Add support for DB_MIGRATION_PASS with Bash special characters + +0.5.5 +Only create app HPA resource if app is enabled + +0.5.4 +Add option to toggle off app deployment (default set to True) + +0.5.3 +Upgrade ingress apiVersion from extensions/v1beta1 to networking.k8s.io/v1 + +0.5.2 +Adding option to specify resources for POD. + +0.5.1 +Adding option to specify initialDelaySeconds for readiness and liveness probes. + +0.5.0 +Removeing migrations from cronjobs and workers since this makes the db state harder to reason about. +Migrations will only be run when the application image is deployed. Remove migration secrets from cronjobs and workers since they +are no longer needed. + +0.4.1 +Removing mysql and elasticsearch subcharts + +0.4.0 +Removing support for development_mysql and development_elasticsearch + +0.3.8 +Default vault url updated to https://vault.prod.edx.org + +0.3.7 +Enabled tls by default for django-ida helm chart +added flag vault.use_tls to disable this behaviour. + +0.3.6 +New version of k8s-cli-util +Move from the older stable url to the newer one for the dev mysql deployment + +0.3.5 +New version of k8s-cli-util + +0.3.4 +Moved autoscaler api endpoints to use apps/v1 instead of apps/v1beta1, requires K8s > 1.10, but should +otherwise be reverse compatible. + +0.3.3 +Ingresses now have a generated number after them to prevent names colliding + +0.3.2 +Added parameter to allow the customization of the healthcheck endpoint with +/health as the default value. +health_check.endpoint: "/health" + +Added a liveness check that is different from the readiness check. + +0.3.1 +Added ability to override the app.port, default is backwards compatible + app.port: 18170 + +0.3.0 +Change defaults for the following variables as it was discovered +that the apps are mostly consistent, it is vault that is inconsistent. + app.migrations.migrate_db_user_env_name: DB_MIGRATION_USER + app.migrations.migrate_db_pass_env_name: DB_MIGRATION_PASS + +0.2.2 +Fix secret render indentation to fit configmap + +0.2.1 +Render config as Yaml instead of as a serialized map + +v0.2.0 +Added the following values to allow user to overwrite migration env names, +since they differ between applications. The following defaults were assigned: + + app.migrations.migrate_db_user_env_name: DATABASE_MIGRATE_USER + app.migrations.migrate_db_pass_env_name: DATABASE_MIGRATE_PASSWORD + +This is a breaking change since the default migrate_db_pass_env_name was +previously: DB_MIGRATION_PASS + +v0.1.1 +Fixed bug where image:tag pairings were not valid + +v0.1.0 +Added overridable issuer for ingresses for the cert issuer. +You will need to add an 'issuer' to each ingress using this version. + +v0.0.4 +Fix bug that resulted in an impossible autoscaling configuration min > max + +v0.0.3 +Allow applications to not specify a role_arn. Removed fake role ARN from service accounts by default. + +v0.0.2 +Added support for arbitrary application environment variables that get passed into all containers running the application image +to support applications that have non standard ENV setups. + +v0.0.1 +Initial commit diff --git a/charts/Chart.lock b/charts/Chart.lock new file mode 100644 index 000000000..b009d458a --- /dev/null +++ b/charts/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: django-ida + repository: https://25c38c15078aaa07ab0119be78db03b720b5e014@raw.githubusercontent.com/edx/helm-repo/master/ + version: 0.8.16 +digest: sha256:0e92ca10d7d40f4c92e8515b0db1c0ba1c6bcc2a43a5863bcbbf7ff75cab9679 +generated: "2023-10-30T14:08:00.791802-04:00" diff --git a/charts/Chart.yaml b/charts/Chart.yaml new file mode 100644 index 000000000..099604e83 --- /dev/null +++ b/charts/Chart.yaml @@ -0,0 +1,3 @@ +name: registrar +version: 0.0.1 +apiVersion: v2 diff --git a/charts/charts/django-ida-0.8.16.tgz b/charts/charts/django-ida-0.8.16.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c36d0b04521c2e9427c2ccd51cd5545a781d8814 GIT binary patch literal 7545 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 P00960?pSVt0KxzOBw6BC literal 0 HcmV?d00001 diff --git a/charts/requirements.lock b/charts/requirements.lock new file mode 100644 index 000000000..e69de29bb diff --git a/charts/requirements.yaml b/charts/requirements.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/charts/templates/app-deployment.yaml b/charts/templates/app-deployment.yaml new file mode 100644 index 000000000..203038053 --- /dev/null +++ b/charts/templates/app-deployment.yaml @@ -0,0 +1,218 @@ +{{ if .Values.app.enabled}} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/instance: {{ .Values.app.name }} + app.kubernetes.io/name: {{ .Values.app.name }} + {{- range $key, $val := .Values.app.additionalLabels }} + {{ $key }}: {{ $val }} + {{- end}} + name: {{ .Values.app.name }} + annotations: + ignore-check.kube-linter.io/no-read-only-root-fs: "Temporarily ignore check no-read-only-root-fs until PSRE-2074 is resolved" +spec: + revisionHistoryLimit: 1 + selector: + matchLabels: + app.kubernetes.io/instance: {{ .Values.app.name }} + app.kubernetes.io/name: {{ .Values.app.name }} + template: + metadata: + labels: + app.kubernetes.io/instance: {{ .Values.app.name }} + app.kubernetes.io/name: {{ .Values.app.name }} + {{- range $key, $val := .Values.app.additionalLabels }} + {{ $key }}: {{ $val }} + {{- end}} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + spec: + containers: + - args: + - "source /vault-api-secrets/secrets/secret.env && \ + exec {{ if .Values.newrelic.enabled }}newrelic-admin run-program{{ end }} \ + {{ regexReplaceAll "^\"|\"$" (quote .Values.app.command) "" }}" + command: + - /bin/bash + - -c + - -- + env: + - name: {{ .Values.app.secret_file_env_name }} + value: /vault-api-secrets/secrets/{{ .Values.app.secret_file_name }} + - name: NEW_RELIC_APP_NAME + valueFrom: + configMapKeyRef: + key: NEW_RELIC_APP_NAME + name: app-cm + - name: NEW_RELIC_LOG_LEVEL + valueFrom: + configMapKeyRef: + key: NEW_RELIC_LOG_LEVEL + name: app-cm + - name: NEW_RELIC_LICENSE_KEY + value: Secret value filled in from secrets.env + {{- range $env := .Values.app.extraEnvs }} + - name: {{ $env.name }} + value: {{ $env.value }} + {{- end }} + image: {{ .Values.app.image.repository }}:{{ .Values.app.image.tag }} + imagePullPolicy: Always + livenessProbe: + initialDelaySeconds: {{ .Values.app.health_check.liveness_probe_initial_delay_seconds}} + exec: + command: + - ls + periodSeconds: 5 + name: {{ .Values.app.name }} + ports: + - containerPort: {{ .Values.app.port }} + name: http + protocol: TCP + readinessProbe: + initialDelaySeconds: {{ .Values.app.health_check.readiness_probe_initial_delay_seconds}} + httpGet: + httpHeaders: + - name: Host + value: {{ .Values.app.health_check.host_header }} + path: {{ .Values.app.health_check.endpoint }} + port: http + timeoutSeconds: 3 + resources: + limits: + cpu: {{ .Values.resources.limits.cpu }} + memory: {{ .Values.resources.limits.memory }} + requests: + cpu: {{ .Values.resources.requests.cpu }} + memory: {{ .Values.resources.requests.memory }} + volumeMounts: + - mountPath: /vault-api-secrets/secrets + name: vault-api-secrets + readOnly: true + initContainers: + - env: + - name: VAULT_ADDR + valueFrom: + configMapKeyRef: + key: VAULT_ADDR + name: app-cm + - name: VAULT_ROLE + valueFrom: + configMapKeyRef: + key: VAULT_ROLE + name: app-cm + - name: TOKEN_DEST_PATH + value: /vault-auth-secrets/secrets/.vault-token + - name: ACCESSOR_DEST_PATH + value: /vault-auth-secrets/secrets/.vault-accessor + image: edxops/vault-kubernetes-authenticator:3b373bc86ade783492b6619552d2b172a6e12a8b + imagePullPolicy: IfNotPresent + name: vault-authenticator + volumeMounts: + - mountPath: /vault-auth-secrets/secrets + name: vault-auth-secrets + resources: + requests: + cpu: "50m" + memory: "48Mi" + limits: + cpu: "100m" + memory: "64Mi" + securityContext: + readOnlyRootFilesystem: true + - command: + - /bin/sh + - -c + - | + set -xe + /bin/consul-template -config /app-cm/vault.hcl -template "/app-cm/{{ .Values.app.secret_file_name }}:/vault-api-secrets/secrets/{{ .Values.app.secret_file_name }}" -once + /bin/consul-template -config /app-cm/vault.hcl -template "/app-cm/secret.env:/vault-api-secrets/secrets/secret.env" -once + /bin/consul-template -config /app-cm/vault.hcl -template "/app-cm/migrate.env:/vault-migrate-secrets/secrets/migrate.env" -once + env: + - name: VAULT_ADDR + valueFrom: + configMapKeyRef: + key: VAULT_ADDR + name: app-cm + image: hashicorp/consul-template:0.20.0-light + imagePullPolicy: IfNotPresent + name: secret-render + volumeMounts: + - mountPath: /vault-auth-secrets/secrets + name: vault-auth-secrets + readOnly: true + - mountPath: /vault-api-secrets/secrets + name: vault-api-secrets + - mountPath: /vault-migrate-secrets/secrets + name: vault-migrate-secrets + - mountPath: /app-cm + name: app-cm + resources: + requests: + cpu: "50m" + memory: "48Mi" + limits: + cpu: "100m" + memory: "64Mi" + securityContext: + readOnlyRootFilesystem: true + {{ if .Values.app.migrations.enabled }} + - args: + - source /vault-migrate-secrets/secrets/migrate.env && {{ .Values.app.migrations.migration_command }} + command: + - /bin/bash + - -c + - -- + env: + - name: {{ .Values.app.secret_file_env_name }} + value: /vault-api-secrets/secrets/{{ .Values.app.secret_file_name }} + - name: DB_MIGRATION_USER + valueFrom: + configMapKeyRef: + key: {{ .Values.app.migrations.migrate_db_user_env_name }} + name: app-cm + optional: true + - name: {{ .Values.app.migrations.migrate_db_pass_env_name }} + value: Secret value filled in from migrate.env + {{- range $env := .Values.app.extraEnvs }} + - name: {{ $env.name }} + value: {{ $env.value }} + {{- end }} + image: {{ .Values.app.image.repository }}:{{ .Values.app.image.tag }} + name: {{ .Values.app.migrations.name}} + resources: + limits: + cpu: 100m + memory: 512Mi + requests: + cpu: 25m + memory: 512Mi + volumeMounts: + - mountPath: /vault-migrate-secrets/secrets + name: vault-migrate-secrets + readOnly: true + - mountPath: /vault-api-secrets/secrets + name: vault-api-secrets + readOnly: true + {{ end }} + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsUser: 1000 + serviceAccountName: {{ .Values.app.service_account_name }} + volumes: + - emptyDir: + medium: Memory + name: vault-auth-secrets + - emptyDir: + medium: Memory + name: vault-api-secrets + - emptyDir: + medium: Memory + name: vault-migrate-secrets + - configMap: + name: app-cm + name: app-cm + +{{ end }} diff --git a/charts/templates/app-hpa.yaml b/charts/templates/app-hpa.yaml new file mode 100644 index 000000000..ee4803b3a --- /dev/null +++ b/charts/templates/app-hpa.yaml @@ -0,0 +1,24 @@ + + +{{ if and .Values.app.enabled .Values.app.autoscaling.enabled}} +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + labels: + app.kubernetes.io/instance: {{ .Values.app.name }} + app.kubernetes.io/name: {{ .Values.app.name }} + {{- range $key, $val := .Values.app.additionalLabels }} + {{ $key }}: {{ $val }} + {{- end}} + name: {{ .Values.app.name }} +spec: + minReplicas: {{ .Values.app.autoscaling.minReplicas }} + maxReplicas: {{ .Values.app.autoscaling.maxReplicas }} + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ .Values.app.name }} + targetCPUUtilizationPercentage: {{ .Values.app.autoscaling.targetCPUUtilizationPercentage }} + +{{ end }} diff --git a/charts/templates/app-ingress.yaml b/charts/templates/app-ingress.yaml new file mode 100644 index 000000000..124eb3851 --- /dev/null +++ b/charts/templates/app-ingress.yaml @@ -0,0 +1,68 @@ +{{- range $index, $ingress := .Values.ingresses }} +{{- with $ }} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + cert-manager.io/cluster-issuer: {{ $ingress.issuer }} + kubernetes.io/tls-acme: "true" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + {{- if $ingress.external_dns }} + kubernetes.io/external-dns: "true" + {{- end }} + {{- if $ingress.external_dns_cloudflare }} + kubernetes.io/external-dns-cloudflare: "true" + {{- end }} + {{- if hasKey $ingress "client-max-body-size" }} + nginx.ingress.kubernetes.io/proxy-body-size: {{ index $ingress "client-max-body-size" }} + {{- end }} + nginx.ingress.kubernetes.io/configuration-snippet: |- + {{- if .Values.newrelic.enabled }} + proxy_set_header X-Queue-Start "t=${msec}"; + {{- end }} + {{- if $ingress.allow_django_admin }} + {{- else }} + server_tokens off; + location /admin { + deny all; + return 403; + } + {{- end }} + labels: + app.kubernetes.io/instance: {{ $.Values.app.name }} + app.kubernetes.io/name: {{ $.Values.app.name }} + {{- range $key, $val := .Values.app.additionalLabels }} + {{ $key }}: {{ $val }} + {{- end}} + {{- /* + Append truncated sha256 of hostname in case we have mutliple ingresses of the same className + */}} + name: {{ $.Values.app.name }}-{{ $ingress.className }}-{{ $ingress.host | sha256sum | trunc 5 }} +spec: + ingressClassName: {{ $ingress.className }} + rules: + - host: {{ $ingress.host }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ $.Values.app.name }} + port: + name: http + tls: + - hosts: + - {{ $ingress.host }} + {{- if $ingress.extra_tls_hosts }} + {{- range $ingress.extra_tls_hosts }} + - {{ . }} + {{- end }} + {{- end }} + {{- /* + Append truncated sha256 of hostname in case we have mutliple ingresses of the same className + */}} + secretName: {{ $.Values.app.name }}-tls-{{ $ingress.className }}-{{ $ingress.host | sha256sum | trunc 5 }} +{{- end }} +{{- end }} diff --git a/charts/templates/app-sa.yaml b/charts/templates/app-sa.yaml new file mode 100644 index 000000000..9d17152d1 --- /dev/null +++ b/charts/templates/app-sa.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- if .Values.app.role_arn }} + annotations: + eks.amazonaws.com/role-arn: {{ .Values.app.role_arn }} + {{- end }} + name: {{ .Values.app.service_account_name }} diff --git a/charts/templates/app-service.yaml b/charts/templates/app-service.yaml new file mode 100644 index 000000000..517867e6a --- /dev/null +++ b/charts/templates/app-service.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/instance: {{ .Values.app.name }} + app.kubernetes.io/name: {{ .Values.app.name }} + name: {{ .Values.app.name }} +spec: + ports: + - name: http + port: {{ .Values.app.port }} + protocol: TCP + targetPort: http + selector: + app.kubernetes.io/instance: {{ .Values.app.name }} + app.kubernetes.io/name: {{ .Values.app.name }} + type: ClusterIP diff --git a/charts/templates/collectstatic-deployment.yaml b/charts/templates/collectstatic-deployment.yaml new file mode 100644 index 000000000..1e4aa1d80 --- /dev/null +++ b/charts/templates/collectstatic-deployment.yaml @@ -0,0 +1,194 @@ + +{{ if .Values.collectstatic.enabled }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/instance: {{ .Values.collectstatic.job_name }} + app.kubernetes.io/name: {{ .Values.collectstatic.job_name }} + {{- range $key, $val := .Values.app.additionalLabels }} + {{ $key }}: {{ $val }} + {{- end}} + name: {{ .Values.collectstatic.job_name }} +spec: + revisionHistoryLimit: 1 + selector: + matchLabels: + app.kubernetes.io/instance: {{ .Values.collectstatic.job_name }} + app.kubernetes.io/name: {{ .Values.collectstatic.job_name }} + template: + metadata: + labels: + app.kubernetes.io/instance: {{ .Values.collectstatic.job_name }} + app.kubernetes.io/name: {{ .Values.collectstatic.job_name }} + {{- range $key, $val := .Values.app.additionalLabels }} + {{ $key }}: {{ $val }} + {{- end}} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + spec: + containers: + - args: + - echo asset upload complete;date; while true; do sleep 999999; done; + command: + - /bin/sh + - -c + - -- + image: busybox:latest + imagePullPolicy: IfNotPresent + name: print-completion-time + resources: + limits: + cpu: 5m + memory: 500Mi + requests: + cpu: 5m + memory: 100Mi + initContainers: + - env: + - name: VAULT_ADDR + valueFrom: + configMapKeyRef: + key: VAULT_ADDR + name: app-cm + - name: VAULT_ROLE + valueFrom: + configMapKeyRef: + key: VAULT_ROLE + name: app-cm + - name: TOKEN_DEST_PATH + value: /vault-auth-secrets/secrets/.vault-token + - name: ACCESSOR_DEST_PATH + value: /vault-auth-secrets/secrets/.vault-accessor + image: edxops/vault-kubernetes-authenticator:latest + imagePullPolicy: IfNotPresent + name: vault-authenticator + volumeMounts: + - mountPath: /vault-auth-secrets/secrets + name: vault-auth-secrets + - command: + - /bin/sh + - -c + - | + set -xe + /bin/consul-template -config /app-cm/vault.hcl -template "/app-cm/{{ .Values.app.secret_file_name }}:/vault-api-secrets/secrets/{{ .Values.app.secret_file_name }}" -once + /bin/consul-template -config /app-cm/vault.hcl -template "/app-cm/secret.env:/vault-api-secrets/secrets/secret.env" -once + env: + - name: VAULT_ADDR + valueFrom: + configMapKeyRef: + key: VAULT_ADDR + name: app-cm + image: hashicorp/consul-template:0.20.0-light + imagePullPolicy: IfNotPresent + name: secret-render + volumeMounts: + - mountPath: /vault-auth-secrets/secrets + name: vault-auth-secrets + readOnly: true + - mountPath: /vault-api-secrets/secrets + name: vault-api-secrets + - mountPath: /app-cm + name: app-cm + - args: + - source /vault-api-secrets/secrets/secret.env && {{ .Values.collectstatic.command }} + command: + - /bin/bash + - -c + - -- + env: + - name: {{ .Values.app.secret_file_env_name }} + value: /vault-api-secrets/secrets/{{ .Values.app.secret_file_name }} + - name: NEW_RELIC_APP_NAME + valueFrom: + configMapKeyRef: + key: NEW_RELIC_APP_NAME + name: app-cm + - name: NEW_RELIC_LOG_LEVEL + valueFrom: + configMapKeyRef: + key: NEW_RELIC_LOG_LEVEL + name: app-cm + - name: NEW_RELIC_LICENSE_KEY + value: Secret value filled in from secrets.env + {{- range $env := .Values.app.extraEnvs }} + - name: {{ $env.name }} + value: {{ $env.value }} + {{- end }} + image: {{ .Values.app.image.repository }}:{{ .Values.app.image.tag }} + imagePullPolicy: Always + name: run-collectstatic + resources: + limits: + cpu: 5m + memory: 500Mi + requests: + cpu: 5m + memory: 100Mi + volumeMounts: + - mountPath: /vault-api-secrets/secrets + name: vault-api-secrets + readOnly: true + - mountPath: /tmp/static + name: app-static + readOnly: false + - args: + - '[[ -n "${S3_ASSET_BUCKET}" ]] && aws s3 sync /tmp/static $S3_ASSET_BUCKET' + command: + - /bin/bash + - -c + - -- + env: + - name: {{ .Values.app.secret_file_env_name }} + value: /vault-api-secrets/secrets/{{ .Values.app.secret_file_name }} + - name: NEW_RELIC_APP_NAME + valueFrom: + configMapKeyRef: + key: NEW_RELIC_APP_NAME + name: app-cm + - name: NEW_RELIC_LOG_LEVEL + valueFrom: + configMapKeyRef: + key: NEW_RELIC_LOG_LEVEL + name: app-cm + - name: NEW_RELIC_LICENSE_KEY + value: Secret value filled in from secrets.env + - name: S3_ASSET_BUCKET + valueFrom: + configMapKeyRef: + key: S3_ASSET_BUCKET + name: app-cm + {{- range $env := .Values.app.extraEnvs }} + - name: {{ $env.name }} + value: {{ $env.value }} + {{- end }} + image: 257477529851.dkr.ecr.us-east-1.amazonaws.com/k8s-cli-utils:latest + imagePullPolicy: IfNotPresent + name: s3-upload + volumeMounts: + - mountPath: /vault-api-secrets/secrets + name: vault-api-secrets + readOnly: true + - mountPath: /tmp/static + name: app-static + readOnly: true + securityContext: + fsGroup: 999 + runAsGroup: 999 + runAsUser: 999 + serviceAccountName: {{ .Values.collectstatic.job_name }} + volumes: + - emptyDir: + medium: Memory + name: vault-auth-secrets + - emptyDir: + medium: Memory + name: vault-api-secrets + - configMap: + name: app-cm + name: app-cm + - emptyDir: + medium: Memory + name: app-static +{{ end }} diff --git a/charts/templates/collectstatic-sa.yaml b/charts/templates/collectstatic-sa.yaml new file mode 100644 index 000000000..e92a5e9c0 --- /dev/null +++ b/charts/templates/collectstatic-sa.yaml @@ -0,0 +1,9 @@ +{{ if .Values.collectstatic.enabled }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + eks.amazonaws.com/role-arn: {{ .Values.collectstatic.asset_write_role }} + name: {{ .Values.collectstatic.job_name }} +{{ end }} diff --git a/charts/templates/configmap.yaml b/charts/templates/configmap.yaml new file mode 100644 index 000000000..970d1659b --- /dev/null +++ b/charts/templates/configmap.yaml @@ -0,0 +1,47 @@ +--- +kind: ConfigMap +metadata: + annotations: {} + labels: {} + name: app-cm +apiVersion: v1 +data: + {{- if .Values.app.migrations.enabled }} + {{ .Values.app.migrations.migrate_db_user_env_name }}: {{ .Values.app.migrations.database_migrate_user }} + {{- end }} + NEW_RELIC_APP_NAME: {{ .Values.newrelic.app_name }} + NEW_RELIC_LOG_LEVEL: {{ .Values.newrelic.log_level }} + S3_ASSET_BUCKET: {{ .Values.collectstatic.s3_bucket }} + VAULT_ADDR: {{ .Values.vault.vault_addr }} + VAULT_ROLE: {{ .Values.vault.vault_role }} + {{ .Values.app.secret_file_name }}: | + --- + {{"{{"}} with secret "{{ .Values.vault.secret_name }}?version={{ .Values.vault.secret_version }}" {{"}}"}} +{{ toYaml .Values.app.config | indent 4}} + {{"{{"}} end {{"}}"}} + migrate.env: |+ + #!/bin/bash + {{"{{"}} with secret "{{ .Values.vault.secret_name }}?version={{ .Values.vault.secret_version }}" {{"}}"}} + export {{ .Values.app.migrations.migrate_db_pass_env_name }}={{"$'{{"}} .Data.data.{{ .Values.app.migrations.migrate_db_pass_env_name }} | replaceAll "\\" "\\\\" | replaceAll "'" "\\'" {{"}}'"}} + {{"{{"}} end {{"}}"}} + + secret.env: | + #!/bin/bash + export NEW_RELIC_LICENSE_KEY={{"{{"}} with secret "{{ .Values.vault.secret_name }}?version={{ .Values.vault.secret_version }}" {{"}}"}}{{"{{"}} .Data.data.NEW_RELIC_LICENSE_KEY {{"}}"}}{{"{{"}} end {{"}}"}} +{{ if .Values.vault.use_tls }} + vault.hcl: | + "vault" = { + "vault_agent_token_file" = "/vault-auth-secrets/secrets/.vault-token" + ssl { + enabled = true + verify = true + ca_cert = "/etc/ssl/cert.pem" + } + } +{{ else }} + vault.hcl: | + "vault" = { + "vault_agent_token_file" = "/vault-auth-secrets/secrets/.vault-token" + } +{{ end }} + diff --git a/charts/templates/cronjobs.yaml b/charts/templates/cronjobs.yaml new file mode 100644 index 000000000..63ad06203 --- /dev/null +++ b/charts/templates/cronjobs.yaml @@ -0,0 +1,121 @@ +{{- range .Values.cronjobs }} +{{- $job := . -}} +{{- with $ }} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ $job.name }} +spec: + concurrencyPolicy: Forbid + schedule: "{{ $job.schedule }}" + jobTemplate: + spec: + template: + spec: + containers: + - args: + - "source /vault-api-secrets/secrets/secret.env && \ + exec {{ if $.Values.newrelic.enabled }}newrelic-admin run-program{{ end }} \ + {{ regexReplaceAll "^\"|\"$" (quote $job.command) "" }}" + command: + - /bin/bash + - -c + - -- + env: + - name: {{ .Values.app.secret_file_env_name }} + value: /vault-api-secrets/secrets/{{ .Values.app.secret_file_name }} + - name: NEW_RELIC_APP_NAME + valueFrom: + configMapKeyRef: + key: NEW_RELIC_APP_NAME + name: app-cm + - name: NEW_RELIC_LOG_LEVEL + valueFrom: + configMapKeyRef: + key: NEW_RELIC_LOG_LEVEL + name: app-cm + - name: NEW_RELIC_LICENSE_KEY + value: Secret value filled in from secrets.env + {{- range $env := .Values.app.extraEnvs }} + - name: {{ $env.name }} + value: {{ $env.value }} + {{- end }} + image: {{ .Values.app.image.repository }}:{{ .Values.app.image.tag }} + imagePullPolicy: Always + name: {{ $job.name }} + resources: + limits: + cpu: {{ (($job.resources).limits).cpu | default "100m" }} + memory: {{ (($job.resources).limits).memory | default "1Gi" }} + requests: + cpu: {{ (($job.resources).requests).cpu | default "25m" }} + memory: {{ (($job.resources).requests).memory | default "512Mi" }} + volumeMounts: + - mountPath: /vault-api-secrets/secrets + name: vault-api-secrets + readOnly: true + initContainers: + - env: + - name: VAULT_ADDR + valueFrom: + configMapKeyRef: + key: VAULT_ADDR + name: app-cm + - name: VAULT_ROLE + valueFrom: + configMapKeyRef: + key: VAULT_ROLE + name: app-cm + - name: TOKEN_DEST_PATH + value: /vault-auth-secrets/secrets/.vault-token + - name: ACCESSOR_DEST_PATH + value: /vault-auth-secrets/secrets/.vault-accessor + image: edxops/vault-kubernetes-authenticator:latest + imagePullPolicy: IfNotPresent + name: vault-authenticator + volumeMounts: + - mountPath: /vault-auth-secrets/secrets + name: vault-auth-secrets + - command: + - /bin/sh + - -c + - | + set -xe + /bin/consul-template -config /app-cm/vault.hcl -template "/app-cm/{{ .Values.app.secret_file_name }}:/vault-api-secrets/secrets/{{ .Values.app.secret_file_name }}" -once + /bin/consul-template -config /app-cm/vault.hcl -template "/app-cm/secret.env:/vault-api-secrets/secrets/secret.env" -once + env: + - name: VAULT_ADDR + valueFrom: + configMapKeyRef: + key: VAULT_ADDR + name: app-cm + image: hashicorp/consul-template:0.20.0-light + imagePullPolicy: IfNotPresent + name: secret-render + volumeMounts: + - mountPath: /vault-auth-secrets/secrets + name: vault-auth-secrets + readOnly: true + - mountPath: /vault-api-secrets/secrets + name: vault-api-secrets + - mountPath: /app-cm + name: app-cm + restartPolicy: Never + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsUser: 1000 + serviceAccountName: {{ .Values.app.service_account_name }} + volumes: + - emptyDir: + medium: Memory + name: vault-auth-secrets + - emptyDir: + medium: Memory + name: vault-api-secrets + - configMap: + name: app-cm + name: app-cm +{{- end }} +{{- end }} diff --git a/charts/templates/workers.yaml b/charts/templates/workers.yaml new file mode 100644 index 000000000..62c283d39 --- /dev/null +++ b/charts/templates/workers.yaml @@ -0,0 +1,158 @@ +{{- range .Values.workers }} +{{- $worker := . -}} +{{- with $ }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/instance: {{ $worker.name }} + app.kubernetes.io/name: {{ $worker.name }} + {{- range $key, $val := .Values.app.additionalLabels }} + {{ $key }}: {{ $val }} + {{- end}} + name: {{ $worker.name }} +spec: + revisionHistoryLimit: 1 + selector: + matchLabels: + app.kubernetes.io/instance: {{ $worker.name }} + app.kubernetes.io/name: {{ $worker.name }} + template: + metadata: + labels: + app.kubernetes.io/instance: {{ $worker.name }} + app.kubernetes.io/name: {{ $worker.name }} + {{- range $key, $val := .Values.app.additionalLabels }} + {{ $key }}: {{ $val }} + {{- end}} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + spec: + containers: + - args: + - "source /vault-api-secrets/secrets/secret.env && \ + exec {{ if $.Values.newrelic.enabled }}newrelic-admin run-program{{ end }} \ + {{ regexReplaceAll "^\"|\"$" (quote $worker.command) "" }}" + command: + - /bin/bash + - -c + - -- + env: + - name: {{ .Values.app.secret_file_env_name }} + value: /vault-api-secrets/secrets/{{ .Values.app.secret_file_name }} + - name: NEW_RELIC_APP_NAME + valueFrom: + configMapKeyRef: + key: NEW_RELIC_APP_NAME + name: app-cm + - name: NEW_RELIC_LOG_LEVEL + valueFrom: + configMapKeyRef: + key: NEW_RELIC_LOG_LEVEL + name: app-cm + - name: NEW_RELIC_LICENSE_KEY + value: Secret value filled in from secrets.env + {{- range $env := .Values.app.extraEnvs }} + - name: {{ $env.name }} + value: {{ $env.value }} + {{- end }} + image: {{ .Values.app.image.repository }}:{{ .Values.app.image.tag }} + imagePullPolicy: Always + name: {{ $worker.name }} + resources: + limits: + cpu: {{ (($worker.resources).limits).cpu | default "125m" }} + memory: {{ (($worker.resources).limits).memory | default "2Gi" }} + requests: + cpu: {{ (($worker.resources).requests).cpu | default "25m" }} + memory: {{ (($worker.resources).requests).memory | default "512Mi" }} + volumeMounts: + - mountPath: /vault-api-secrets/secrets + name: vault-api-secrets + readOnly: true + initContainers: + - env: + - name: VAULT_ADDR + valueFrom: + configMapKeyRef: + key: VAULT_ADDR + name: app-cm + - name: VAULT_ROLE + valueFrom: + configMapKeyRef: + key: VAULT_ROLE + name: app-cm + - name: TOKEN_DEST_PATH + value: /vault-auth-secrets/secrets/.vault-token + - name: ACCESSOR_DEST_PATH + value: /vault-auth-secrets/secrets/.vault-accessor + image: edxops/vault-kubernetes-authenticator:latest + imagePullPolicy: IfNotPresent + name: vault-authenticator + volumeMounts: + - mountPath: /vault-auth-secrets/secrets + name: vault-auth-secrets + - command: + - /bin/sh + - -c + - | + set -xe + /bin/consul-template -config /app-cm/vault.hcl -template "/app-cm/{{ .Values.app.secret_file_name }}:/vault-api-secrets/secrets/{{ .Values.app.secret_file_name }}" -once + /bin/consul-template -config /app-cm/vault.hcl -template "/app-cm/secret.env:/vault-api-secrets/secrets/secret.env" -once + env: + - name: VAULT_ADDR + valueFrom: + configMapKeyRef: + key: VAULT_ADDR + name: app-cm + image: hashicorp/consul-template:0.20.0-light + imagePullPolicy: IfNotPresent + name: secret-render + volumeMounts: + - mountPath: /vault-auth-secrets/secrets + name: vault-auth-secrets + readOnly: true + - mountPath: /vault-api-secrets/secrets + name: vault-api-secrets + - mountPath: /app-cm + name: app-cm + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsUser: 1000 + serviceAccountName: {{ .Values.app.service_account_name }} + volumes: + - emptyDir: + medium: Memory + name: vault-auth-secrets + - emptyDir: + medium: Memory + name: vault-api-secrets + - configMap: + name: app-cm + name: app-cm + +{{ if .Values.app.autoscaling.enabled}} +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + labels: + app.kubernetes.io/instance: {{ $worker.name }} + app.kubernetes.io/name: {{ $worker.name }} + {{- range $key, $val := .Values.app.additionalLabels }} + {{ $key }}: {{ $val }} + {{- end}} + name: {{ $worker.name }} +spec: + minReplicas: {{ $worker.minReplicas }} + maxReplicas: {{ $worker.maxReplicas }} + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $worker.name }} + targetCPUUtilizationPercentage: {{ $worker.targetCPUUtilizationPercentage }} + {{ end }} + {{- end }} + {{- end }} diff --git a/charts/values.yaml b/charts/values.yaml new file mode 100644 index 000000000..35f1cec7f --- /dev/null +++ b/charts/values.yaml @@ -0,0 +1,130 @@ +registrar: + app: + name: registrar + role_arn: arn:aws:iam::708756755355:role/development-edx-registrar + command: 'gunicorn --workers=2 --name registrar -c /edx/app/registrar/registrar/docker_gunicorn_configuration.py --log-file - --max-requests=1000 registrar.wsgi:application' + + port: 18734 + + secret_file_env_name: REGISTRAR_CFG + secret_file_name: registrar.yml + service_account_name: registrar + migrations: + name: registrar-migrations + enabled: true + database_migrate_user: db-user + + autoscaling: + enabled: false + minReplicas: 3 + maxReplicas: 10 + targetCPUUtilizationPercentage: 50 + + health_check: + liveness_probe_initial_delay_seconds: 30 + readiness_probe_initial_delay_seconds: 30 + + # FILL-ME-IN + config: + API_ROOT: https://api.development.edx.org/registrar + BACKEND_SERVICE_EDX_OAUTH2_KEY: '{{ .Data.data.BACKEND_SERVICE_EDX_OAUTH2_KEY }}' + BACKEND_SERVICE_EDX_OAUTH2_PROVIDER_URL: https://courses.development.edx.org/oauth2 + BACKEND_SERVICE_EDX_OAUTH2_SECRET: '{{ .Data.data.BACKEND_SERVICE_EDX_OAUTH2_SECRET}}' + CACHES: + default: + BACKEND: django.core.cache.backends.memcached.MemcachedCache + KEY_PREFIX: registrar + LOCATION: + - development-edx-registrar.6sxrym.0001.use1.cache.amazonaws.com + - development-edx-registrar.6sxrym.0002.use1.cache.amazonaws.com + CELERY_ALWAYS_EAGER: false + CELERY_BROKER_HOSTNAME: edx-development-queues.6sxrym.ng.0001.use1.cache.amazonaws.com:6379 + CELERY_BROKER_PASSWORD: '' + CELERY_BROKER_TRANSPORT: redis + CELERY_BROKER_USER: '' + CELERY_BROKER_VHOST: 0 + CELERY_DEFAULT_EXCHANGE: registrar + CELERY_DEFAULT_QUEUE: registrar.default + CELERY_DEFAULT_ROUTING_KEY: registrar + CORS_ORIGIN_WHITELIST: + - https://development-edx-registrar.edx.org + - https://registrar.development.edx.org + - https://program-console.development.edx.org + CSRF_COOKIE_SECURE: true + CSRF_TRUSTED_ORIGINS: + - .edx.org + CSRF_TRUSTED_ORIGINS_WITH_SCHEMES: + - https://*.edx.org + DATABASES: + default: + ATOMIC_REQUESTS: false + CONN_MAX_AGE: 60 + ENGINE: django.db.backends.mysql + HOST: mysql.mysql + NAME: db + OPTIONS: + connect_timeout: 10 + init_command: SET sql_mode='STRICT_TRANS_TABLES' + PASSWORD: '{{ .Data.data.DATABASE_DEFAULT_PASSWORD }}' + PORT: '3306' + USER: db-user + DISCOVERY_BASE_URL: https://discovery.development.edx.org + EDX_DRF_EXTENSIONS: + OAUTH2_USER_INFO_URL: https://courses.development.edx.org/oauth2/user_info + JWT_AUTH: + JWT_AUTH_COOKIE_HEADER_PAYLOAD: development-edx-jwt-cookie-header-payload + JWT_AUTH_COOKIE_SIGNATURE: development-edx-jwt-cookie-signature + JWT_ISSUERS: + - AUDIENCE: '{{ .Data.data.JWT_ISSUERS_0_AUDIENCE }}' + ISSUER: https://courses.development.edx.org/oauth2 + SECRET_KEY: '{{ .Data.data.JWT_ISSUERS_0_SECRET_KEY }}' + JWT_PUBLIC_SIGNING_JWK_SET: '{"keys": [{"n": "hcm7899L5XQ6AVNYwNo3Yu-rx47f0FMAN3am6WgurbDulrcCIfhyTivzpnuOY0W-2tntlR51j4hHzywSSCqdOgG1MZLfVSJwVpVUhd9ROLuIRbifXyRJ1_d7C_L3YZdyYqFY7k8W5f62UqCePxVCh-zCKtkfjCJkhRujgDw4YeL63j80We48T0LYK5ZSRBOEj2N4fjbzsi9T2d1qCBaLvXwgYzMnUTc8mch6JMP8HWsrgqV4kkPyP3il_IgRARV5BF5cdJbUg2-__5QirmLF16xl9j0vo9yLyBnqlYZXWYjFOECI7FatHLGQDT5TopXWT4YF82_aZSNuIQUoDY8hDQ", + "kty": "RSA", "e": "AQAB", "kid": "lmsdevelopment002"}]}' + LMS_BASE_URL: https://courses.development.edx.org + MEDIA_STORAGE_BACKEND: + AWS_DEFAULT_ACL: null + AWS_LOCATION: '' + AWS_QUERYSTRING_AUTH: true + AWS_QUERYSTRING_EXPIRE: 3600 + DEFAULT_FILE_STORAGE: storages.backends.s3boto3.S3Boto3Storage + REGISTRAR_BUCKET: development-edx-registrar + PROGRAM_REPORTS_BUCKET: development-edx-program-reports + REGISTRAR_SERVICE_USER: registrar_service_user + SECRET_KEY: '{{ .Data.data.SECRET_KEY }}' + SEGMENT_KEY: '{{ .Data.data.SEGMENT_KEY }}' + SESSION_COOKIE_SECURE: true + SOCIAL_AUTH_EDX_OAUTH2_ISSUER: https://courses.development.edx.org + SOCIAL_AUTH_EDX_OAUTH2_KEY: '{{ .Data.data.SOCIAL_AUTH_EDX_OAUTH2_KEY }}' + SOCIAL_AUTH_EDX_OAUTH2_LOGOUT_URL: https://courses.development.edx.org/logout + SOCIAL_AUTH_EDX_OAUTH2_SECRET: '{{ .Data.data.SOCIAL_AUTH_EDX_OAUTH2_SECRET }}' + SOCIAL_AUTH_EDX_OAUTH2_URL_ROOT: https://courses.development.edx.org + STATIC_ROOT: /tmp/static + + workers: [] + # - name: registrar-worker + # command: celery -A registrar worker --loglevel info + # minReplicas: 3 + # maxReplicas: 6 + # targetCPUUtilizationPercentage: 100 + + newrelic: + enabled: false + app_name: development-edx-registrar + log_level: info + + collectstatic: + enabled: false + + vault: + enabled: true + vault_role: registrar + vault_addr: http://development-vault.vault:8200 + secret_name: kv/registrar + secret_version: 1 + + ingresses: + - host: registrar-eks.development.edx.org + class: nginx + issuer: selfsigning-issuer + + cronjobs: [] diff --git a/devspace.yaml b/devspace.yaml new file mode 100644 index 000000000..5d09341a7 --- /dev/null +++ b/devspace.yaml @@ -0,0 +1,98 @@ +version: v2beta1 +name: registrargit + +# This is a list of `pipelines` that DevSpace can execute (you can define your own) +pipelines: + # This is the pipeline for the main command: `devspace dev` (or `devspace run-pipeline dev`) + dev: + run: |- + run_dependencies --all # 1. Deploy any projects this project needs (see "dependencies") + ensure_pull_secrets --all # 2. Ensure pull secrets + create_deployments --all # 3. Deploy Helm charts and manifests specfied as "deployments" + start_dev app # 4. Start dev mode "app" (see "dev" section) + # You can run this pipeline via `devspace deploy` (or `devspace run-pipeline deploy`) + deploy: + run: |- + run_dependencies --all # 1. Deploy any projects this project needs (see "dependencies") + ensure_pull_secrets --all # 2. Ensure pull secrets + build_images --all -t $(git describe --always) # 3. Build, tag (git commit hash) and push all images (see "images") + create_deployments --all # 4. Deploy Helm charts and manifests specfied as "deployments" + +# This is a list of `images` that DevSpace can build for this project +# We recommend to skip image building during development (devspace dev) as much as possible +images: + app: + image: edxops/registrar + dockerfile: ./Dockerfile + +# This is a list of `deployments` that DevSpace can create for this project +deployments: + app: + # This deployment uses `helm` but you can also define `kubectl` deployments or kustomizations + helm: + # We are deploying this project with the Helm chart you provided + chart: + name: .devspace/chart-repo/argocd/applications/registrar + # Under `values` we can define the values for this Helm chart used during `helm install/upgrade` + # You may also use `valuesFiles` to load values from files, e.g. valuesFiles: ["values.yaml"] + valuesFiles: + - ./charts/development-config.yaml + vault: + helm: + chart: + name: .devspace/chart-repo/argocd/applications/vault + valuesFiles: + - .devspace/chart-repo/argocd/applications/vault/development.yaml + + vault-bootstrapper: + kubectl: + kustomize: true + manifests: + - ./vault-development-bootstrapper/ + +# This is a list of `dev` containers that are based on the containers created by your deployments +dev: + app: + # Search for the container that runs this image + imageSelector: edxops/registrar + # Sync files between the local filesystem and the development container + sync: + - path: ./ + uploadExcludeFile: .dockerignore + # Open a terminal and use the following command to start it + terminal: + command: ./devspace_start.sh + # Inject a lightweight SSH server into the container (so your IDE can connect to the remote dev env) + ssh: + enabled: true + # Make the following commands from my local machine available inside the dev container + proxyCommands: + - command: devspace + - command: kubectl + - command: helm + - gitCredentials: true + # Forward the following ports to be able access your application via localhost + ports: + - port: "18734" + # Open the following URLs once they return an HTTP status code other than 502 or 503 + open: + - url: http://localhost:18734 + +# Use the `commands` section to define repeatable dev workflows for this project +commands: + migrate-db: + command: |- + echo 'This is a cross-platform, shared command that can be used to codify any kind of dev task.' + echo 'Anyone using this project can invoke it via "devspace run migrate-db"' +hooks: + - events: + - before:deploy + command: if [ -d '.devspace/chart-repo/.git' ]; then cd ".devspace/chart-repo" && git pull origin master; else mkdir -p .devspace/chart-repo; git clone --single-branch --branch master git@github.com:edx/edx-internal.git .devspace/chart-repo; fi + +# Define dependencies to other projects with a devspace.yaml +# dependencies: +# api: +# git: https://... # Git-based dependencies +# tag: v1.0.0 +# ui: +# path: ./ui # Path-based dependencies (for monorepos) diff --git a/devspace_start.sh b/devspace_start.sh new file mode 100755 index 000000000..d681506bc --- /dev/null +++ b/devspace_start.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set +e # Continue on errors + +COLOR_BLUE="\033[0;94m" +COLOR_GREEN="\033[0;92m" +COLOR_RESET="\033[0m" + +# Print useful output for user +echo -e "${COLOR_BLUE} + %########% + %###########% ____ _____ + %#########% | _ \ ___ __ __ / ___/ ____ ____ ____ ___ + %#########% | | | | / _ \\\\\ \ / / \___ \ | _ \ / _ | / __// _ \\ + %#############% | |_| |( __/ \ V / ____) )| |_) )( (_| |( (__( __/ + %#############% |____/ \___| \_/ \____/ | __/ \__,_| \___\\\\\___| + %###############% |_| + %###########%${COLOR_RESET} + + +Welcome to your development container! + +This is how you can work with it: +- Files will be synchronized between your local machine and this container +- Some ports will be forwarded, so you can access this container via localhost +- Run \`${COLOR_GREEN}python main.py${COLOR_RESET}\` to start the application +" + +# Set terminal prompt +export PS1="\[${COLOR_BLUE}\]devspace\[${COLOR_RESET}\] ./\W \[${COLOR_BLUE}\]\\$\[${COLOR_RESET}\] " +if [ -z "$BASH" ]; then export PS1="$ "; fi + +# Include project's bin/ folder in PATH +export PATH="./bin:$PATH" + +# Open shell +bash --norc