diff --git a/.gitea/workflows/upload-helm.yml b/.gitea/workflows/upload-helm.yml new file mode 100644 index 0000000..c0e4a7f --- /dev/null +++ b/.gitea/workflows/upload-helm.yml @@ -0,0 +1,25 @@ +name: Upload Helm Chart +run-name: Uploading helm chart +on: + push: + tags: ['*'] + +jobs: + Explore-Gitea-Actions: + runs-on: shell + env: + HELM_CHART_VERSION: "${{ github.ref_name }}" + steps: + - uses: actions/checkout@v2 + - name: Set up Helm + uses: azure/setup-helm@v1 + with: + version: 'v3.0.0' + - run: 'sed -i "s/version:.*/version: \"${HELM_CHART_VERSION}\"/" ./Chart/Chart.yaml' + - run: helm package ./Chart + - name: Upload Helm Chart + run: | + curl --request POST \ + --user ${{ secrets.HELM_PKG_UPLOAD_USER }}:${{ secrets.HELM_PKG_UPLOAD_PASS }} \ + --form "chart=@umbraco-${HELM_CHART_VERSION}.tgz" \ + https://git.cloudyne.io/api/packages/helm/helm/api/charts \ No newline at end of file diff --git a/Chart/Chart.yaml b/Chart/Chart.yaml new file mode 100644 index 0000000..5b71148 --- /dev/null +++ b/Chart/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: "v2" +name: "umbraco" +description: "Umbraco sites on Azure hosting" +version: "0.1.0" +appVersion: "1.0.0" diff --git a/Chart/templates/_helpers.tpl b/Chart/templates/_helpers.tpl new file mode 100644 index 0000000..cae9752 --- /dev/null +++ b/Chart/templates/_helpers.tpl @@ -0,0 +1,72 @@ +{{/* +Define the application name and fullname +*/}} + +{{- define "..name" -}} +{{- .Values.site.name | trunc 63 }} +{{- end }} + +{{- define "..fullname" -}} +{{ include "..name" . }} +{{- end }} + +{{- define "..domains" }} +- {{ .Values.site.primaryDomain }} +{{- if .Values.site.additionalDomains }}{{ .Values.site.additionalDomains | toYaml }}{{- end }} +{{- end }} + +{{- define "..resourcelimits" -}} +resources: + limits: + cpu: {{ .Values.site.resources.cpu.peak }} + memory: {{ .Values.site.resources.mem.peak }} + requests: + cpu: {{ .Values.site.resources.cpu.avg }} + memory: {{ .Values.site.resources.mem.avg }} +{{- end }} + +{{/* +Define the chart name and version +*/}} + +{{- define "..chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Define the chart common labels +*/}} +{{- define "..labels" -}} +helm.sh/chart: {{ include "..chart" . }} +app.kubernetes.io/name: {{ include "..name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +cloudyne.systems/customer: {{ .Values.customer.legalName | replace " " "-" | replace "," "" | trunc 63 | trimSuffix "-" | quote }} +cloudyne.systems/customer-legal-id: '{{ .Values.customer.legalId }}' +cloudyne.systems/site: {{ .Values.site.primaryDomain | quote }} +{{- end }} +{{- define "..selector-labels" -}} +cloudyne.systems/customer: {{ .Values.customer.legalName | replace " " "-" | replace "," "" | trunc 63 | trimSuffix "-" | quote }} +cloudyne.systems/site: {{ .Values.site.primaryDomain | quote }} +cloudyne.systems/component: site +{{- end }} +{{- define "..affinity-labels" -}} +podAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: cloudyne.systems/customer + operator: In + values: + - {{ .Values.customer.legalName | replace " " "-" | replace "," "" | trunc 63 | trimSuffix "-" | quote }} + - key: cloudyne.systems/site + operator: In + values: + - {{ .Values.site.domain | quote }} + - key: cloudyne.systems/component + operator: In + values: + - "site" + topologyKey: kubernetes.io/hostname +{{- end }} \ No newline at end of file diff --git a/Chart/templates/deployment.yaml b/Chart/templates/deployment.yaml new file mode 100644 index 0000000..aa66ced --- /dev/null +++ b/Chart/templates/deployment.yaml @@ -0,0 +1,67 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + {{ include "..labels" . | nindent 4 }} + name: {{ include "..fullname" . }} +spec: + replicas: 1 + selector: + matchLabels: + {{ include "..selector-labels" . | nindent 6 }} + template: + metadata: + labels: + {{ include "..selector-labels" . | nindent 8 }} + spec: + containers: + - name: umbraco + envFrom: + {{- range $sec := .Values.secrets }} + {{- if eq $sec.type "env"}} + - secretRef: + name: {{ $sec.name }} + {{- end }} + {{- end }} + {{- range $esec := .Values.externalSecrets }} + {{- if eq $esec.type "env"}} + - secretRef: + name: {{ $esec.ref.target }} + {{- end }} + {{- end }} + {{- range $cfgm := .Values.configMaps }} + {{- if eq $cfgm.type "env"}} + - configMapRef: + name: {{ $cfgm.name }} + {{- end }} + {{- end }} + image: {{ .Values.site.image }} + imagePullPolicy: Always + imagePullSecrets: + {{ range $secret := .Values.secrets }} + {{- if eq $secret.type "docker" }} + - name: {{ $secret.name }} + {{- end }} + {{- end }} + {{- range $esecret := .Values.externalSecrets }} + {{- if eq $esecret.type "docker" }} + - name: {{ $esecret.ref.target }} + {{- end }} + {{- end }} + ports: + - containerPort: 8123 + name: http + protocol: TCP + resources: + limits: + cpu: {{ .Values.site.resources.cpu.peak | default "1000m" }} + memory: {{ .Values.site.resources.mem.peak | default "512Mi" }} + requests: + cpu: {{ .Values.site.resources.cpu.min | default "100m" }} + memory: {{ .Values.site.resources.mem.min | default "256Mi" }} + securityContext: + allowPrivilegeEscalation: false + runAsUser: 0 + dnsPolicy: ClusterFirst + restartPolicy: Always + diff --git a/Chart/templates/externalsecret.yaml b/Chart/templates/externalsecret.yaml new file mode 100644 index 0000000..f897dce --- /dev/null +++ b/Chart/templates/externalsecret.yaml @@ -0,0 +1,37 @@ +{{- if and .Values.externalSecrets }} + {{- range .Values.externalSecrets }} +--- +apiVersion: v1 +kind: ExternalSecret +metadata: + name: {{ include "..fullname" $ }}-{{ .name }} + labels: + {{- include "..labels" $ | nindent 4 }} +spec: + refreshInterval: {{ .refreshInterval | default "10h" }} + secretStoreRef: + {{- if .ref.clusterSecretStore }} + kind: ClusterSecretStore + name: {{ .ref.clusterSecretStore }} + {{- else }} + kind: SecretStore + name: {{ .ref.secretStore }} + namespace: {{ .ref.secretStoreNamespace }} + {{- end }} + target: + name: {{ include "..fullname" $ }}-exts-{{ .name }} + template: + {{- if eq .type "docker"}} + type: kubernetes.io/dockerconfigjson + {{- end }} + metadata: + labels: + app.kubernetes.io/managed-by: External-Secrets + data: + {{- range $v := .items }} + - secretKey: {{ $v.target }} + remoteRef: + key: {{ $v.source }} + {{- end }} + {{- end }} +{{- end }} diff --git a/Chart/templates/ingress.yaml b/Chart/templates/ingress.yaml new file mode 100644 index 0000000..65e9882 --- /dev/null +++ b/Chart/templates/ingress.yaml @@ -0,0 +1,43 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + cert-manager.io/cluster-issuer: {{ .Values.site.certificateIssuer | default "zssl-production" }} + labels: {{- include "..labels" . | nindent 4 }} + name: {{ include "..fullname" . }} +spec: + ingressClassName: nginx + rules: + - host: {{ .Values.site.primaryDomain }} + http: + paths: + - backend: + service: + name: {{ include "..fullname" . }} + port: + number: 8123 + path: / + pathType: Prefix + {{- if .Values.site.additionalDomains }} + {{- range $domain := .Values.site.additionalDomains }} + - host: {{ $domain }} + http: + paths: + - backend: + service: + name: {{ include "..fullname" $ }} + port: + number: 8123 + path: / + pathType: Prefix + {{- end }} + {{- end }} + tls: + - hosts: + - {{ .Values.site.primaryDomain }} + {{- if .Values.site.additionalDomains }} + {{- range $domain := .Values.site.additionalDomains }} + - {{ $domain }} + {{- end }} + {{- end }} + secretName: tls-{{ include "..fullname" . }} diff --git a/Chart/templates/secret.yaml b/Chart/templates/secret.yaml new file mode 100644 index 0000000..fc594f3 --- /dev/null +++ b/Chart/templates/secret.yaml @@ -0,0 +1,21 @@ +{{- if .Values.secrets }} + {{- range $sec := .Values.secrets }} +--- +apiVersion: v1 +kind: Secret +{{- if or (eq $sec.type "env") (eq $sec.type "file") }} +type: Opaque +{{- else if eq $sec.type "docker" }} +type: kubernetes.io/dockerconfigjson +{{- end }} +metadata: + name: {{ include "..fullname" $ }}-{{ $sec.name }} + labels: + {{- include "..labels" $ | nindent 4 }} +data: + {{- range $item := $sec.values }} + - name: {{ $item.name }} + value: {{ $item.value | b64enc | quote }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/Chart/templates/service.yaml b/Chart/templates/service.yaml new file mode 100644 index 0000000..f93a78a --- /dev/null +++ b/Chart/templates/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "..fullname" . }} +spec: + internalTrafficPolicy: Cluster + ports: + - port: 8123 + protocol: TCP + targetPort: 8123 + selector: {{ include "..selector-labels" . | nindent 4 }} + sessionAffinity: None + type: ClusterIP diff --git a/Chart/values.yaml b/Chart/values.yaml new file mode 100644 index 0000000..a10cbb0 --- /dev/null +++ b/Chart/values.yaml @@ -0,0 +1,97 @@ +customer: + name: "customer-name" + legalName: "Customer Name Ltd" + legalId: "123456-7890" + timezone: "Europe/Stockholm" + +site: + name: "site-name" + primaryDomain: "primarydomain.com" + + additionalDomains: + - "additionaldomain.com" + + image: "image:tag" + + certificateIssuer: "zssl-production" + + resources: + cpu: + peak: 1000m + avg: 500m + mem: + peak: 1Gi + avg: 500Mi + storage: 10Gi + +secrets: + - name: "test-secret" + type: "env" + values: + - name: "ABC_TEST" + value: "123" + - name: "DEF_TEST" + value: | + Hello World + - name: "test-file" + type: "file" + values: + - name: "test-file.txt" + path: "/tmp/test-file.txt" + value: | + Hello World + - name: "test-docker" + type: "docker" + values: + - name: ".dockerconfigjson" + value: | + { + "auths": { + "https://index.docker.io/v1/": { + "auth": "dXNlcm5hbWU6cGFzc3dvcmQ=" + } + } + } + +configMaps: + - name: "test-configmap" + type: "file" + values: + - name: "test-configmap.txt" + path: "/tmp/test-configmap.txt" + value: | + Hello World + - name: "test-env" + type: "env" + values: + - name: "ABC_TEST" + value: "123" + - name: "DEF_TEST" + value: | + Hello World + +externalSecrets: + - name: "ex-gl-secret" + type: "env" + ref: + secretStore: "" + clusterSecretStore: "az-cluster-store" + target: "global-secrets" + items: + - source: secret/SMTP-USER + target: SMTP_USER + - source: secret/SMTP-PASSWORD + target: SMTP_PASS + - source: secret/SMTP-HOST + target: SMTP_HOST + - name: "ex-pull-secret" + type: "docker" + ref: + secretStore: "" + secretStoreNamespace: "" + clusterSecretStore: "az-cluster-store" + target: "pull-secret" + items: + - source: secret/PULL-TOKEN + target: dockerconfigjson + \ No newline at end of file diff --git a/README.md b/README.md index 02942a8..747a9e3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,135 @@ # umbraco +Helm chart for Umbraco deployments -Helm chart for Umbraco deployments \ No newline at end of file +## Values +```yaml +customer: + # The customer name (DNS-Compliant) + name: customerdomain-tld + + # The customer legal name + legalName: Customer Name Ltd. + + # The customer legal ID + legalId: 112233-4455 + + # The customer timezone + timezone: "Europe/Stockholm" + +site: + name: "customerdomain-tld" + + primaryDomain: customerdomain.tld + additionalDomains: + - www.customerdomain.tld + + image: "cr.cloudyne.io/umbraco/umbraco:8.14.0" + + user: + ASPNETCORE_ENVIRONMENT: "Production" + AppSettings__Recaptcha__SecretKey: "" + AppSettings__Recaptcha__SiteKey: "" + ConnectionStrings__umbracoDbDSN: "" + Umbraco__Storage__AzureBlob__Media__ConnectionString: "" + Umbraco__Storage__AzureBlob__Media__ContainerName: "" + Umbraco__Storage__CDN__REMOVEMEDIAFROMPATH: "true" + Umbraco__Storage__CDN__URL: "" + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: "false" + Umbraco__CMS__WebRouting__UmbracoApplicationUrl: "" + VIRTUAL_HOST: "" + + + external: + Umbraco__CMS__Global__Smtp__From: bm8tcmVwbHlAbm92ZWEuY2xvdWQ= + Umbraco__CMS__Global__Smtp__Host: c210cC5jbG91ZHluZS5uZXQ= + Umbraco__CMS__Global__Smtp__Password: a2tpbjZ6THVXSlJ2M2ppUzhlTTJWNTlU + Umbraco__CMS__Global__Smtp__Port: MjUyNQ== + Umbraco__CMS__Global__Smtp__Username: dWthZC1kZXY= + + + settings: + ASPNETCORE_ENVIRONMENT: "Production" + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: "false" + VIRTUAL_HOST: "" + AppSettings: + Recaptcha: + SecretKey: "" + SiteKey: + ConnectionStrings: + umbraocDbDSN: "" + Umbraco: + CMS: + Global: + Smtp: + From: "" + Host: "" + Password: "" + Port: 587 + Username: "" + Storage: + AzureBlob: + Media: + ConnectionString: + ContainerName: "" + CDN: + REMOVEMEDIAFROMPATH: "true" + URL: "" + + + + resources: + cpu: + peak: 1000m + avg: 500m + mem: + peak: 1Gi + avg: 500Mi + storage: 5Gi + + domain: "customerdomain.tld" + additionalDomains: [] + +secrets: [] +# - name: "abc-secret +# type: "env" | "file" | "docker" +# values: +# - name: "SMTP_USER" +# value: | +# Hello World +configMaps: [] +# - name: "abc-configmap" +# type: "file" | "env" +# values: +# - name: "SMTP_USER" +# value: | +# Hello World +externalSecrets: [] +# - name: "ex-gl-secret" +# type: "env" +# ref: +# secretStore: "" +# clusterSecretStore: "az-cluster-store" +# target: "global-secrets" +# items: +# - source: secret/SMTP-USER +# target: SMTP_USER +# - source: secret/SMTP-PASSWORD +# target: SMTP_PASS +# - source: secret/SMTP-HOST +# target: SMTP_HOST +# - name: "ex-pull-secret" +# type: "docker" +# ref: +# secretStore: "" +# secretStoreNamespace: "" +# clusterSecretStore: "az-cluster-store" +# target: "pull-secret" +# items: +# - source: secret/PULL-TOKEN +# target: dockerconfigjson + + + + + +``` \ No newline at end of file