From 5ad8193e6d9db1da98948df8e500751a976e83b0 Mon Sep 17 00:00:00 2001 From: Jack Kleeman Date: Tue, 13 Feb 2024 12:21:11 +0000 Subject: [PATCH] Move to helm package for operator --- .github/workflows/release.yml | 21 +- README.md | 7 +- charts/restate-operator-helm/.helmignore | 23 +++ charts/restate-operator-helm/Chart.yaml | 6 + .../crds/restateclusters.yaml | 1 + .../templates/_helpers.tpl | 22 ++ .../templates/deployment.yaml | 53 +++++ .../restate-operator-helm/templates/rbac.yaml | 78 +++++++ .../templates/service.yaml | 21 ++ .../templates/servicemonitor.yaml | 44 ++++ charts/restate-operator-helm/values.yaml | 50 +++++ deploy/operator.pkl | 193 ------------------ 12 files changed, 324 insertions(+), 195 deletions(-) create mode 100644 charts/restate-operator-helm/.helmignore create mode 100644 charts/restate-operator-helm/Chart.yaml create mode 120000 charts/restate-operator-helm/crds/restateclusters.yaml create mode 100644 charts/restate-operator-helm/templates/_helpers.tpl create mode 100644 charts/restate-operator-helm/templates/deployment.yaml create mode 100644 charts/restate-operator-helm/templates/rbac.yaml create mode 100644 charts/restate-operator-helm/templates/service.yaml create mode 100644 charts/restate-operator-helm/templates/servicemonitor.yaml create mode 100644 charts/restate-operator-helm/values.yaml delete mode 100644 deploy/operator.pkl diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e391f9b..151fdda 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,10 +11,29 @@ jobs: uses: ./.github/workflows/docker.yml secrets: inherit + build-helm-chart: + runs-on: ubuntu-latest + steps: + - name: Write release version + id: version + run: | + VERSION=${GITHUB_REF_NAME#v} + echo Version: $VERSION + echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" + - name: Push helm chart + uses: appany/helm-oci-chart-releaser@v0.3.0 + with: + name: restate-operator-helm + repository: restatedev + tag: ${{ steps.version.outputs.VERSION }} + registry: ghcr.io + registry_username: ${{ github.actor }} + registry_password: ${{ secrets.GITHUB_TOKEN }} + publish-release: name: Publish release runs-on: ubuntu-latest - needs: [build-docker-image] + needs: [build-docker-image build-helm-chart] steps: - name: Checkout diff --git a/README.md b/README.md index 2cec655..bb4e3b4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # Restate Operator +## Installing +```bash +helm install restate-operator ghcr.io/restatedev/restate-operator-helm --namespace restate-operator --create-namespace +``` + ## Releasing -1. Update the image version in deploy/operator.pkl and the version in Cargo.toml eg to `0.0.2` +1. Update the app version in charts/restate-operator/Chart.yaml and the version in Cargo.toml eg to `0.0.2` 2. Push a new tag `v0.0.2` 3. Accept the draft release once the workflow finishes diff --git a/charts/restate-operator-helm/.helmignore b/charts/restate-operator-helm/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/charts/restate-operator-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/charts/restate-operator-helm/Chart.yaml b/charts/restate-operator-helm/Chart.yaml new file mode 100644 index 0000000..184dd5d --- /dev/null +++ b/charts/restate-operator-helm/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: restate-operator +description: An operator for Restate clusters +type: application +version: "0.0.1" +appVersion: "0.0.1" diff --git a/charts/restate-operator-helm/crds/restateclusters.yaml b/charts/restate-operator-helm/crds/restateclusters.yaml new file mode 120000 index 0000000..851134f --- /dev/null +++ b/charts/restate-operator-helm/crds/restateclusters.yaml @@ -0,0 +1 @@ +../../../crd/crd.yaml \ No newline at end of file diff --git a/charts/restate-operator-helm/templates/_helpers.tpl b/charts/restate-operator-helm/templates/_helpers.tpl new file mode 100644 index 0000000..a965ba4 --- /dev/null +++ b/charts/restate-operator-helm/templates/_helpers.tpl @@ -0,0 +1,22 @@ +{{- define "controller.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "controller.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- $name | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "controller.labels" -}} +{{- include "controller.selectorLabels" . }} +app.kubernetes.io/name: {{ include "controller.name" . }} +app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }} +{{- end }} + +{{- define "controller.selectorLabels" -}} +app: {{ include "controller.name" . }} +{{- end }} + +{{- define "controller.tag" -}} +{{- .Values.version | default .Chart.AppVersion }} +{{- end }} diff --git a/charts/restate-operator-helm/templates/deployment.yaml b/charts/restate-operator-helm/templates/deployment.yaml new file mode 100644 index 0000000..b25685a --- /dev/null +++ b/charts/restate-operator-helm/templates/deployment.yaml @@ -0,0 +1,53 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "controller.fullname" . }} + labels: + {{- include "controller.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "controller.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "controller.selectorLabels" . | nindent 8 }} + annotations: + kubectl.kubernetes.io/default-container: {{ .Chart.Name }} + {{- if .Values.podAnnotations }} + {{- toYaml .Values.podAnnotations | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "controller.fullname" . }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + image: {{ .Values.image.repository }}:{{ include "controller.tag" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + env: + - name: RUST_LOG + value: {{ .Values.logging.env_filter }} + {{- with .Values.env }} + {{- toYaml . | nindent 8 }} + {{- end }} + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 5 + periodSeconds: 5 diff --git a/charts/restate-operator-helm/templates/rbac.yaml b/charts/restate-operator-helm/templates/rbac.yaml new file mode 100644 index 0000000..e0b87b9 --- /dev/null +++ b/charts/restate-operator-helm/templates/rbac.yaml @@ -0,0 +1,78 @@ +{{- if .Values.serviceAccount.create }} +--- +# Scoped service account +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "controller.fullname" . }} + labels: + {{- include "controller.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +automountServiceAccountToken: true + {{- end }} + +--- +# Access for the service account +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "controller.fullname" . }} +rules: + - resources: + - restateclusters + - restateclusters/status + - restateclusters/finalizers + verbs: + - get + - list + - watch + - patch + apiGroups: + - restate.dev + - resources: + - events + verbs: + - create + apiGroups: + - events.k8s.io + - resources: + - namespaces + - services + - serviceaccounts + - networkpolicies + - statefulsets + - persistentvolumeclaims + verbs: + - get + - list + - watch + - create + - patch + apiGroups: + - '' + - apps + - networking.k8s.io + - resources: + - statefulsets + verbs: + - delete + apiGroups: + - apps +--- +# Binding the role to the account +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "controller.fullname" . }} +subjects: + - kind: ServiceAccount + namespace: {{ .Release.Namespace }} + name: {{ include "controller.fullname" . }} +roleRef: + kind: ClusterRole + name: {{ include "controller.fullname" . }} + apiGroup: rbac.authorization.k8s.io diff --git a/charts/restate-operator-helm/templates/service.yaml b/charts/restate-operator-helm/templates/service.yaml new file mode 100644 index 0000000..fcf2df5 --- /dev/null +++ b/charts/restate-operator-helm/templates/service.yaml @@ -0,0 +1,21 @@ +--- +# Expose the http port of the service +apiVersion: v1 +kind: Service +metadata: + name: {{ include "controller.fullname" . }} + labels: + {{- include "controller.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: 8080 + protocol: TCP + name: http + selector: + app: {{ include "controller.fullname" . }} diff --git a/charts/restate-operator-helm/templates/servicemonitor.yaml b/charts/restate-operator-helm/templates/servicemonitor.yaml new file mode 100644 index 0000000..50bd6c4 --- /dev/null +++ b/charts/restate-operator-helm/templates/servicemonitor.yaml @@ -0,0 +1,44 @@ +{{- if .Values.serviceMonitor.enabled }} +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "controller.fullname" . }} + labels: + {{- include "controller.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + endpoints: + - port: http + {{- with .Values.serviceMonitor.interval }} + interval: {{ . }} + {{- end }} + {{- with .Values.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ . }} + {{- end }} + honorLabels: true + path: {{ .Values.serviceMonitor.path }} + scheme: {{ .Values.serviceMonitor.scheme }} + {{- with .Values.serviceMonitor.relabelings }} + relabelings: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.serviceMonitor.metricRelabelings }} + metricRelabelings: + {{- toYaml . | nindent 6 }} + {{- end }} + jobLabel: {{ include "controller.fullname" . }} + selector: + matchLabels: + {{- include "controller.selectorLabels" . | nindent 6 }} + namespaceSelector: + matchNames: + - {{ .Values.namespace }} + {{- with .Values.serviceMonitor.targetLabels }} + targetLabels: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/restate-operator-helm/values.yaml b/charts/restate-operator-helm/values.yaml new file mode 100644 index 0000000..4b9f2f0 --- /dev/null +++ b/charts/restate-operator-helm/values.yaml @@ -0,0 +1,50 @@ +replicaCount: 1 +nameOverride: "" +version: "" # pin a specific version + +image: + repository: ghcr.io/restatedev/restate-operator + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +serviceAccount: + create: true + annotations: {} +podAnnotations: {} + +podSecurityContext: + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" +securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 3000 + + +logging: + env_filter: info,restate=debug + +env: [] + +service: + type: ClusterIP + port: 80 + +resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 50m + memory: 100Mi + +serviceMonitor: + enabled: false + path: /metrics + scheme: http diff --git a/deploy/operator.pkl b/deploy/operator.pkl deleted file mode 100644 index 1a8dc9e..0000000 --- a/deploy/operator.pkl +++ /dev/null @@ -1,193 +0,0 @@ -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/K8sObject.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/K8sResource.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/apps/v1/Deployment.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/core/v1/EnvVar.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/core/v1/PodSpec.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/core/v1/ResourceRequirements.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/core/v1/SecurityContext.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/core/v1/Service.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/core/v1/ServiceAccount.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/rbac/v1/ClusterRole.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/api/rbac/v1/ClusterRoleBinding.pkl" -import "package://pkg.pkl-lang.org/pkl-k8s/k8s@1.0.1#/apimachinery/pkg/apis/meta/v1/ObjectMeta.pkl" - -name = "restate-operator" -namespace = name -baseImage = "restatedev/restate-operator" -imageVersion = "0.0.1" - -selectorLabels: Mapping = new { - ["app"] = name -} - -labels: Mapping = new { - ["app.kubernetes.io/name"] = name - ["app.kubernetes.io/version"] = imageVersion - ...selectorLabels -} - -metadata: ObjectMeta = new { - name = module.name - namespace = module.namespace - labels = module.labels -} - -podSecurityContext: PodSpec.PodSecurityContext = new { - runAsNonRoot = true - runAsUser = 1000 - runAsGroup = 3000 - fsGroup = 2000 - fsGroupChangePolicy = "OnRootMismatch" - seccompProfile { - type = "RuntimeDefault" - } -} - -securityContext: SecurityContext = new { - capabilities { - drop { - "ALL" - } - } - allowPrivilegeEscalation = false - readOnlyRootFilesystem = true -} - -resourceRequirements: ResourceRequirements = new { - limits { - ["cpu"] = "200m" - ["memory"] = 256.mib - } - requests { - ["cpu"] = "50m" - ["memory"] = 100.mib - } -} - -logFilter = "info,kube=debug,controller=debug" - -env: Listing = new { - new { - name = "RUST_LOG" - value = logFilter - } -} - -resources = new Mapping { - ["Deployment"] = new Deployment { - metadata = module.metadata - spec { - replicas = 1 - selector { - matchLabels = selectorLabels - } - template { - metadata { - labels = module.labels - } - spec { - serviceAccountName = name - securityContext = podSecurityContext - containers { - new { - name = module.name - env = module.env - image = "\(baseImage):\(imageVersion)" - securityContext = module.securityContext - resources = module.resourceRequirements - ports { - new { - name = "http" - containerPort = 8080 - protocol = "TCP" - } - } - readinessProbe { - httpGet { - path = "/health" - port = "http" - } - initialDelaySeconds = 5 - periodSeconds = 5 - } - } - } - } - } - } - } - ["Service"] = new Service { - metadata = module.metadata - spec { - type = "ClusterIP" - selector = selectorLabels - ports { - new { - name = "http" - port = 8080 - targetPort = 8080 - protocol = "TCP" - } - } - } - } - ["ServiceAccount"] = new ServiceAccount { - metadata = module.metadata - automountServiceAccountToken = true - } - ["ClusterRole"] = new ClusterRole { - metadata = module.metadata - rules { - new { - apiGroups { "restate.dev" } - resources { "restateclusters" "restateclusters/status" "restateclusters/finalizers" } - verbs { "get" "list" "watch" "patch" } - } - new { - apiGroups { "events.k8s.io" } - resources { "events" } - verbs { "create" } - } - new { // owned objects - apiGroups { "" "apps" "networking.k8s.io" } - resources { - "namespaces" "services" "serviceaccounts" - "networkpolicies" "statefulsets" "persistentvolumeclaims" - } - verbs { "get" "list" "watch" "create" "patch" } - } - new { // we delete statefulsets as part of pvc resizing - apiGroups { "apps" } - resources { "statefulsets" } - verbs { "delete" } - } - } - } - ["ClusterRoleBinding"] = new ClusterRoleBinding { - metadata = module.metadata - subjects { - new { - kind = "ServiceAccount" - namespace = module.namespace - name = module.name - } - } - roleRef { - kind = "ClusterRole" - name = module.name - apiGroup = "rbac.authorization.k8s.io" - } - } -} - -output { - renderer = new YamlRenderer { - converters = (K8sObject.output.renderer as YamlRenderer).converters - isStream = true - } - value = new Listing { - for (_, resource in resources) { - resource - } - } -}