diff --git a/.github/workflows/check-dock.yaml b/.github/workflows/check-doc.yaml similarity index 94% rename from .github/workflows/check-dock.yaml rename to .github/workflows/check-doc.yaml index a35dc1762..7fad211ac 100644 --- a/.github/workflows/check-dock.yaml +++ b/.github/workflows/check-doc.yaml @@ -15,7 +15,7 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Run helm-docs - run: docker run --rm --volume "$(pwd):/helm-docs" -u "$(id -u)" jnorwood/helm-docs:latest + run: docker run --rm --volume "$(pwd):/helm-docs" -u "$(id -u)" jnorwood/helm-docs:v1.12.0 - name: Check for unstaged changes run: | diff --git a/charts/espresso/Chart.lock b/charts/espresso/Chart.lock new file mode 100644 index 000000000..4f83d8d21 --- /dev/null +++ b/charts/espresso/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: file://../common + version: 1.0.1 +digest: sha256:16871b89b082fb3f3fa0c9bb12fa86f144272c29639386c444322cbaa454e17b +generated: "2024-01-24T14:35:14.068637+01:00" diff --git a/charts/espresso/Chart.yaml b/charts/espresso/Chart.yaml new file mode 100644 index 000000000..f2d839a11 --- /dev/null +++ b/charts/espresso/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v2 +name: espresso +version: 1.0.0 +appVersion: 1.0.0 +kubeVersion: "^1.23.0-0" +description: A Helm chart that combines Kubernetes manifests and scripts to deploy Espresso Sequencer AVS nodes. +type: application +keywords: + - eigenlayer + - AVS + - espresso +home: https://docs.espressosys.com/sequencer +dependencies: + - name: common + repository: file://../common + version: 1.0.1 +maintainers: + - name: matilote + - name: AntiD2ta diff --git a/charts/espresso/README.md b/charts/espresso/README.md new file mode 100644 index 000000000..cbca2a437 --- /dev/null +++ b/charts/espresso/README.md @@ -0,0 +1,99 @@ +# espresso + +![Version: 1.0.0](https://img.shields.io/badge/Version-1.0.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) + +A Helm chart that combines Kubernetes manifests and scripts to deploy Espresso Sequencer AVS nodes. + +**Homepage:** + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| matilote | | | +| AntiD2ta | | | + +## Requirements + +Kubernetes: `^1.23.0-0` + +| Repository | Name | Version | +|------------|------|---------| +| file://../common | common | 1.0.1 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| externalSecrets.enabled | bool | `false` | | +| externalSecrets.secretStoreRef.kind | string | `"SecretStore"` | | +| externalSecrets.secretStoreRef.name | string | `"secretStoreRef"` | | +| fullnameOverride | string | `""` | Provide a name to substitute for the full names of resources | +| global.namespaceOverride | string | `""` | | +| global.persistence | object | `{"accessModes":["ReadWriteOnce"],"annotations":{},"size":"150Gi","storageClassName":""}` | Whether or not to allocate persistent volume disk for the data directory. In case of node failure, the node data directory will still persist. | +| global.serviceAccount | object | `{"annotations":{},"create":false}` | Service account ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ | +| image.pullPolicy | string | `"IfNotPresent"` | | +| image.repository | string | `"ghcr.io/espressosystems/espresso-sequencer/sequencer"` | | +| image.tag | string | `"main"` | | +| ingress.annotations | object | `{}` | | +| ingress.className | string | `""` | | +| ingress.enabled | bool | `false` | | +| ingress.hosts | list | `[]` | Hostnames. Can be provided if Ingress is enabled. | +| ingress.labels | object | `{}` | | +| ingress.paths | list | `[]` | Paths to use for ingress rules By default, the Service created by this chart is used as the target Service for the Ingress. If not defined the following default object will be used: - path: "/" port: 8000 pathType: "ImplementationSpecific" serviceName: "" | +| ingress.routePrefix | string | `"/"` | Route Prefix. Can skip it if any item of path has the path defined. | +| ingress.tls | list | `[]` | TLS configuration for Ingress Secret must be manually created in the namespace | +| initImage | object | `{"pullPolicy":"IfNotPresent","repository":"bitnami/kubectl","tag":"1.28"}` | Init image is used to manage which secrets the pod should use. | +| keystoreCLI | object | `{"db":{"host":"","secretId":"","user":""},"image":{"pullPolicy":"IfNotPresent","repository":"nethermindeth/espresso-keystore-cli","tag":"v0.1.1"},"projectId":"","pv":{"secretId":""}}` | Keystore-CLI settings. Used to manage keys on Secret Store. | +| nodes.da.command[0] | string | `"sequencer"` | | +| nodes.da.command[10] | string | `"query"` | | +| nodes.da.command[1] | string | `"--"` | | +| nodes.da.command[2] | string | `"storage-sql"` | | +| nodes.da.command[3] | string | `"--"` | | +| nodes.da.command[4] | string | `"http"` | | +| nodes.da.command[5] | string | `"--"` | | +| nodes.da.command[6] | string | `"catchup"` | | +| nodes.da.command[7] | string | `"--"` | | +| nodes.da.command[8] | string | `"status"` | | +| nodes.da.command[9] | string | `"--"` | | +| nodes.da.externalSecrets.data | list | `[]` | | +| nodes.da.replicaCount | int | `0` | | +| nodes.da.resources.requests.memory | string | `"12000Mi"` | | +| nodes.da.sqlStorage | bool | `true` | | +| nodes.da.volumeMount | bool | `false` | | +| nodes.normal.command[0] | string | `"sequencer"` | | +| nodes.normal.command[1] | string | `"--"` | | +| nodes.normal.command[2] | string | `"http"` | | +| nodes.normal.command[3] | string | `"--"` | | +| nodes.normal.command[4] | string | `"catchup"` | | +| nodes.normal.command[5] | string | `"--"` | | +| nodes.normal.command[6] | string | `"status"` | | +| nodes.normal.externalSecrets.data | list | `[]` | | +| nodes.normal.replicaCount | int | `1` | | +| nodes.normal.resources.requests.memory | string | `"12000Mi"` | | +| nodes.normal.sqlStorage | bool | `false` | | +| nodes.normal.volumeMount | bool | `true` | | +| nodes_config.ESPRESSO_SEQUENCER_API_PORT | int | `80` | | +| nodes_config.ESPRESSO_SEQUENCER_BASE_FEE | int | `0` | | +| nodes_config.ESPRESSO_SEQUENCER_CDN_ENDPOINT | string | `"cdn.cappuccino.testnet.espresso.network:1737"` | | +| nodes_config.ESPRESSO_SEQUENCER_CHAIN_ID | int | `0` | | +| nodes_config.ESPRESSO_SEQUENCER_L1_PROVIDER | string | `""` | | +| nodes_config.ESPRESSO_SEQUENCER_MAX_BLOCK_SIZE | string | `"30000000"` | | +| nodes_config.ESPRESSO_SEQUENCER_ORCHESTRATOR_URL | string | `"https://orchestrator.cappuccino.testnet.espresso.network"` | | +| nodes_config.ESPRESSO_SEQUENCER_STATE_PEERS | string | `"https://query.cappuccino.testnet.espresso.network"` | | +| nodes_config.ESPRESSO_SEQUENCER_STORAGE_PATH | string | `"/mount/sequencer/store/"` | | +| nodes_config.ESPRESSO_STATE_RELAY_SERVER_URL | string | `"https://state-relay.cappuccino.testnet.espresso.network"` | | +| nodes_config.RUST_LOG | string | `"warn,libp2p=off"` | | +| nodes_config.RUST_LOG_FORMAT | string | `"json"` | | +| rbac.create | bool | `true` | | +| rbac.name | string | `""` | The name of the role to use. If not set and create is true, a name is generated using the fullname template | +| rbac.rules | list | `[{"apiGroups":[""],"resources":["secrets"],"verbs":["create","get","list","watch","delete"]}]` | Required Role rules | +| rbac.rules[0] | object | `{"apiGroups":[""],"resources":["secrets"],"verbs":["create","get","list","watch","delete"]}` | Required to create Sequencer env Secret. | +| service.annotations | object | `{}` | | +| service.type | string | `"ClusterIP"` | | +| serviceAccount | object | `{"annotations":{},"name":""}` | Service account ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ | +| serviceAccount.annotations | object | `{}` | Annotations to add to the service account | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/espresso/templates/_helpers.yaml b/charts/espresso/templates/_helpers.yaml new file mode 100644 index 000000000..bb104c1fd --- /dev/null +++ b/charts/espresso/templates/_helpers.yaml @@ -0,0 +1,8 @@ +{{/* +Create a formatted image string with repository and tag +*/}} +{{- define "espresso.build_image_name" -}} +{{- $repository := index . 0 -}} +{{- $tag := index . 1 -}} +{{- printf "%s:%s" $repository $tag | quote -}} +{{- end -}} diff --git a/charts/espresso/templates/clusterrole.yaml b/charts/espresso/templates/clusterrole.yaml new file mode 100644 index 000000000..069733ab0 --- /dev/null +++ b/charts/espresso/templates/clusterrole.yaml @@ -0,0 +1,10 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "common.names.clusterRoleName" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} +rules: +{{- toYaml .Values.rbac.clusterRules | nindent 0 }} +{{- end }} diff --git a/charts/espresso/templates/clusterrolebinding.yaml b/charts/espresso/templates/clusterrolebinding.yaml new file mode 100644 index 000000000..c045d4fc8 --- /dev/null +++ b/charts/espresso/templates/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "common.names.clusterRoleName" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "common.names.clusterRoleName" . }} +subjects: + - kind: ServiceAccount + name: {{ include "common.names.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/espresso/templates/configmap.yaml b/charts/espresso/templates/configmap.yaml new file mode 100644 index 000000000..410fdbdd5 --- /dev/null +++ b/charts/espresso/templates/configmap.yaml @@ -0,0 +1,64 @@ +{{- range $type, $specs := .Values.nodes }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "common.names.fullname" $ }}-init-{{ $type }} + labels: + {{- include "common.labels.standard" $ | nindent 4 }} +data: + init.sh: | + function process_key() { + local key_var=$1 + local prefix=$2 + local final_var=$3 + + # Extract the value for the specified indexed key from the environment + local key_full=$(env | grep -e $key_var) + + # Check if the string starts with the prefix and remove it + local key_value + if [[ $key_full == $prefix* ]]; then + key_value="${key_full#$prefix}" + else + key_value="$key_full" + fi + + # Construct the final key name + # Replace all occurrences of key_var in key_value with final_var + local final_key="${key_value//$key_var/${final_var}}" + + echo "$final_key" + } + # Get the index from the hostname + index=$(hostname | grep -o -E "[0-9]+$") + echo "Detected Pod Index: $index" + + # Construct the variable names + # ENV is imported from the external secret manager + # ENV keys are in the format of state_key_var and staking_key_var with the index as the suffix + state_key_var="ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY_$index" + staking_key_var="ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY_$index" + key_prefix="{{ $specs.secrets.sequencerSecretKey }}=" + {{- if $specs.sqlStorage }} + postgres_host_var="ESPRESSO_SEQUENCER_POSTGRES_HOST" + postgres_user_var="ESPRESSO_SEQUENCER_POSTGRES_USER" + postgres_pass_var="ESPRESSO_SEQUENCER_POSTGRES_PASSWORD" + postgres_prefix="{{ $specs.secrets.postgresSecretKey }}=" + {{- end }} + + # Process each key and write to the .env file + echo "$(process_key $state_key_var $key_prefix "ESPRESSO_SEQUENCER_PRIVATE_STATE_KEY")" > /etc/espresso/.env + echo "$(process_key $staking_key_var $key_prefix "ESPRESSO_SEQUENCER_PRIVATE_STAKING_KEY")" >> /etc/espresso/.env + {{- if $specs.sqlStorage }} + echo "$(process_key $postgres_host_var $postgres_prefix "ESPRESSO_SEQUENCER_POSTGRES_HOST")" >> /etc/espresso/.env + echo "$(process_key $postgres_user_var $postgres_prefix "ESPRESSO_SEQUENCER_POSTGRES_USER")" >> /etc/espresso/.env + echo "$(process_key $postgres_pass_var $postgres_prefix "ESPRESSO_SEQUENCER_POSTGRES_PASSWORD")" >> /etc/espresso/.env + {{- end }} + echo "Dot env file content:" + cat /etc/espresso/.env + + # Create Secret with .env file + # kubectl delete secret sequencer-env-secrets-{{ $type }}-$index --ignore-not-found + # kubectl create secret generic sequencer-env-secrets-{{ $type }}-$index --from-env-file=/etc/espresso/.env +{{- end }} diff --git a/charts/espresso/templates/ingress.yaml b/charts/espresso/templates/ingress.yaml new file mode 100644 index 000000000..d070acaaf --- /dev/null +++ b/charts/espresso/templates/ingress.yaml @@ -0,0 +1,52 @@ +{{- if .Values.ingress.enabled -}} +--- + {{- $baseServiceName := include "common.names.fullname" . -}} + {{- $routePrefix := .Values.ingress.routePrefix | default "/" -}} + {{- $defaultPath := list (dict "path" $routePrefix "port" 8000 "pathType" "ImplementationSpecific") -}} + {{- $paths := .Values.ingress.paths | default $defaultPath -}} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else -}} +apiVersion: networking.k8s.io/v1beta1 +{{- end }} +kind: Ingress +metadata: + {{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4}} + {{- end }} + name: {{ include "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- with .Values.ingress.labels }} + {{ toYaml . | indent 4 }} + {{- end }} + {{- with $.Values.global.namespaceOverride }} + namespace: {{ . }} + {{- end }} +spec: + {{- with .Values.ingress.className }} + ingressClassName: {{ . }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ .serviceName | default $baseServiceName }} + port: + number: {{ .port }} + {{- end }} + {{- with .host }} + host: {{ . | quote }} + {{- end }} + {{- end }} + {{- with .Values.ingress.tls }} + tls: + {{ tpl (toYaml . | indent 4) . }} + {{- end }} +{{- end }} diff --git a/charts/espresso/templates/role.yaml b/charts/espresso/templates/role.yaml new file mode 100644 index 000000000..8b8575ba9 --- /dev/null +++ b/charts/espresso/templates/role.yaml @@ -0,0 +1,10 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} +rules: + {{- toYaml .Values.rbac.rules | nindent 0 }} +{{- end }} diff --git a/charts/espresso/templates/rolebinding.yaml b/charts/espresso/templates/rolebinding.yaml new file mode 100644 index 000000000..bbf92cd45 --- /dev/null +++ b/charts/espresso/templates/rolebinding.yaml @@ -0,0 +1,15 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "common.names.serviceAccountName" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "common.names.serviceAccountName" . }} +subjects: + - kind: ServiceAccount + name: {{ include "common.names.serviceAccountName" . }} +{{- end }} diff --git a/charts/espresso/templates/secret.yaml b/charts/espresso/templates/secret.yaml new file mode 100644 index 000000000..fb3e6305d --- /dev/null +++ b/charts/espresso/templates/secret.yaml @@ -0,0 +1,42 @@ +{{- if .Values.externalSecrets.enabled }} +{{- range $type, $specs := .Values.nodes }} +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: "{{ $.Values.externalSecrets.name }}-{{ $type }}" + {{- with $.Values.global.namespaceOverride }} + namespace: {{ . }} + {{- end }} +spec: + refreshInterval: "1h" + secretStoreRef: + name: {{ $.Values.externalSecrets.secretStoreRef.name }} + kind: {{ $.Values.externalSecrets.secretStoreRef.kind }} + target: + name: "{{ $.Values.externalSecrets.name }}-{{ $type }}" + creationPolicy: Owner + {{- with $specs.secrets.data }} + data: + {{- . | toYaml | trim | nindent 2 }} + {{- end }} +{{- end }} +{{- else }} +{{- range $type, $specs := .Values.nodes }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: "k8s-espresso-sequencer-secrets-{{ $type }}" + {{- with $.Values.global.namespaceOverride }} + namespace: {{ . }} + {{- end }} +type: Opaque +data: + {{- with $specs.secrets.data }} + {{- range $key, $value := . }} + {{ $key }}: {{ $value | b64enc }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/espresso/templates/service.yaml b/charts/espresso/templates/service.yaml new file mode 100644 index 000000000..6dea21014 --- /dev/null +++ b/charts/espresso/templates/service.yaml @@ -0,0 +1,28 @@ +{{- range $type, $specs := .Values.nodes }} +--- +apiVersion: v1 +kind: Service +metadata: + name: "{{ include "common.names.fullname" $ }}-{{ $type }}" + labels: + {{- include "common.labels.standard" $ | nindent 4 }} + pod: "{{ include "common.names.fullname" $ }}" + type: api + {{- with $.Values.service.annotations }} + annotations: + {{ toYaml . | nindent 4 | trim }} + {{- end }} + {{- with $.Values.global.namespaceOverride }} + namespace: {{ . }} + {{- end }} +spec: + type: {{ $.Values.service.type }} # ClusterIP, NodePort, LoadBalancer, or ExternalName + ports: + - name: api + port: {{ $.Values.nodes_config.ESPRESSO_SEQUENCER_API_PORT }} + targetPort: api + protocol: TCP + selector: + {{- include "common.labels.matchLabels" $ | nindent 4 }} + type: {{ $type }} +{{- end }} diff --git a/charts/espresso/templates/serviceaccount.yaml b/charts/espresso/templates/serviceaccount.yaml new file mode 100644 index 000000000..6896a029a --- /dev/null +++ b/charts/espresso/templates/serviceaccount.yaml @@ -0,0 +1,16 @@ +--- +{{- if .Values.global.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "common.names.serviceAccountName" $ }} + labels: + {{- include "common.labels.standard" $ | nindent 4 }} + {{- with .Values.global.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $.Values.global.namespaceOverride }} + namespace: {{ . }} + {{- end }} +{{- end }} diff --git a/charts/espresso/templates/servicemonitor.yaml b/charts/espresso/templates/servicemonitor.yaml new file mode 100644 index 000000000..b0bfb8464 --- /dev/null +++ b/charts/espresso/templates/servicemonitor.yaml @@ -0,0 +1,41 @@ +{{- range $type, $specs := .Values.nodes }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: "{{ include "common.names.fullname" $ }}-{{ $type }}" + {{- if $.Values.serviceMonitor.namespace }} + namespace: {{ $.Values.serviceMonitor.namespace }} + {{- else }} + namespace: {{ $.Release.Namespace | quote }} + {{- end }} + labels: + {{- include "common.labels.standard" $ | nindent 4 }} + {{- if $.Values.serviceMonitor.additionalLabels }} + {{- toYaml $.Values.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} +spec: + endpoints: + - port: api + path: /v0/status/metrics + {{- if $.Values.serviceMonitor.interval }} + interval: {{ $.Values.serviceMonitor.interval }} + {{- end }} + {{- if $.Values.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ $.Values.serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if $.Values.serviceMonitor.honorLabels }} + honorLabels: {{ $.Values.serviceMonitor.honorLabels }} + {{- end }} + {{- if $.Values.serviceMonitor.relabelings }} + relabelings: {{- toYaml $.Values.serviceMonitor.relabelings | nindent 6 }} + {{- end }} + {{- if $.Values.serviceMonitor.metricRelabelings }} + metricRelabelings: {{- toYaml $.Values.serviceMonitor.metricRelabelings | nindent 6 }} + {{- end }} + namespaceSelector: + matchNames: + - {{ $.Release.Namespace }} + selector: + matchLabels: + {{- include "common.labels.matchLabels" $ | nindent 6 }} +{{- end }} \ No newline at end of file diff --git a/charts/espresso/templates/statefulset.yaml b/charts/espresso/templates/statefulset.yaml new file mode 100644 index 000000000..faa8fe0d2 --- /dev/null +++ b/charts/espresso/templates/statefulset.yaml @@ -0,0 +1,141 @@ +{{- range $type, $specs := .Values.nodes }} +--- +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" $ }} +kind: StatefulSet +metadata: + name: "{{ include "common.names.fullname" $ }}-{{ $type }}" + {{- with $.Values.global.namespaceOverride }} + namespace: {{ . }} + {{- end }} +spec: + serviceName: "{{ include "common.names.fullname" $ }}-{{ $type }}" + replicas: {{ $specs.replicaCount }} + selector: + matchLabels: + {{- include "common.labels.matchLabels" $ | nindent 6 }} + type: {{ $type }} + template: + metadata: + labels: + {{- include "common.labels.matchLabels" $ | nindent 8 }} + type: {{ $type }} + spec: + serviceAccountName: {{ include "common.names.fullname" $ }} + {{- if $.Values.externalSecrets.enabled }} + initContainers: + - name: keygen # Generates keys for the sequencer + image: {{ include "espresso.build_image_name" (list $.Values.image.repository $.Values.image.tag) }} + imagePullPolicy: {{ $.Values.image.pullPolicy }} + command: ["keygen", "-o", "/keys", "-n", "{{ $specs.replicaCount }}"] + volumeMounts: + - name: keys + mountPath: /keys + - name: keystore-cli-pv-keys # Updates Secret Manager with private keys in the proper format + image: {{ include "espresso.build_image_name" (list $.Values.keystoreCLI.image.repository $.Values.keystoreCLI.image.tag) }} + imagePullPolicy: {{ $.Values.keystoreCLI.image.pullPolicy }} + args: ["pv-keys"] + env: + - name: KEYS_PATH + value: /keys + - name: PROJECT_ID + value: {{ (required "Project ID is required" $.Values.keystoreCLI.projectId) | quote }} + - name: SECRET_ID + value: {{ required "Secret ID is required" $.Values.keystoreCLI.pv.secretId }} + volumeMounts: + - name: keys + mountPath: /keys + {{- if $specs.sqlStorage }} + - name: keystore-cli-db-keys # Updates Secret Manager with DB credentials in the proper format + image: {{ include "espresso.build_image_name" (list $.Values.keystoreCLI.image.repository $.Values.keystoreCLI.image.tag) }} + imagePullPolicy: {{ $.Values.keystoreCLI.image.pullPolicy }} + args: ["db-keys"] + env: + - name: PROJECT_ID + value: {{ (required "Project ID is required" $.Values.keystoreCLI.projectId) | quote }} + - name: SECRET_ID + value: {{ required "Secret ID is required" $.Values.keystoreCLI.db.secretId }} + - name: SEQUENCER_POSTGRES_HOST + value: {{ (required "DB host is required" $.Values.keystoreCLI.db.host) | quote }} + - name: SEQUENCER_POSTGRES_USER + value: {{ (required "DB user is required" $.Values.keystoreCLI.db.user) | quote }} + volumeMounts: + - name: keys + mountPath: /keys + {{- end }} + - name: init-setenv # Sets keys environment variables for the sequencer + image: {{ include "espresso.build_image_name" (list $.Values.initImage.repository $.Values.initImage.tag) }} + imagePullPolicy: {{ $.Values.initImage.pullPolicy }} + command: ['/bin/bash', '/scripts/init.sh'] + envFrom: + - secretRef: + {{- if $.Values.externalSecrets.enabled }} + name: {{ $.Values.externalSecrets.name }}-{{ $type }} + {{- else }} + name: k8s-espresso-sequencer-secrets-{{ $type }} + {{- end }} + volumeMounts: + - name: init-env + mountPath: /etc/espresso + - name: scripts-init + mountPath: /scripts + {{- end }} + containers: + - name: "sequencer-{{ $type }}" + image: {{ include "espresso.build_image_name" (list $.Values.image.repository $.Values.image.tag) }} + imagePullPolicy: {{ $.Values.image.pullPolicy }} + command: {{ toYaml $specs.command | nindent 12 }} + env: + {{- range $key, $value := $.Values.nodes_config }} + - name: {{ $key }} + value: {{ required (printf "%s is required" $key) $value | quote }} + {{- end }} + - name: ESPRESSO_SEQUENCER_IDENTITY_NODE_NAME + value: {{ $specs.nodeName }}-$(hostname | awk -F'-' '{print $NF}') + - name: ESPRESSO_SEQUENCER_IDENTITY_OPERATING_SYSTEM + value: $(uname -sr) + {{- with $.Values.global.networkType }} + - name: ESPRESSO_SEQUENCER_IDENTITY_NETWORK_TYPE + value: {{ . }} + {{- end }} + - name: ESPRESSO_SEQUENCER_KEY_FILE + value: /etc/espresso/.env + ports: + - name: api + containerPort: {{ $.Values.nodes_config.ESPRESSO_SEQUENCER_API_PORT }} + {{- if or $specs.volumeMount }} + volumeMounts: + - name: consensus-data + mountPath: {{ $.Values.nodes_config.ESPRESSO_SEQUENCER_STORAGE_PATH }} + - name: init-env + mountPath: /etc/espresso + {{- end }} + {{- with $specs.resources }} + resources: + {{ toYaml . | nindent 12 | trim }} + {{- end }} + volumes: + - name: keys + emptyDir: {} + - name: init-env + emptyDir: {} + - name: scripts-init + configMap: + name: {{ include "common.names.fullname" $ }}-init-{{ $type }} + volumeClaimTemplates: + {{- if $specs.volumeMount }} + - metadata: + name: consensus-data + labels: + {{- include "common.labels.statefulset" $ | nindent 8 }} + {{- with $.Values.global.persistence.annotations }} + annotations: + {{ toYaml . | nindent 10 | trim }} + {{- end }} + spec: + accessModes: {{ $.Values.global.persistence.accessModes }} + storageClassName: {{ $.Values.global.persistence.storageClassName }} + resources: + requests: + storage: {{ $.Values.global.persistence.size | quote }} + {{- end }} +{{- end }} diff --git a/charts/espresso/values.yaml b/charts/espresso/values.yaml new file mode 100644 index 000000000..655f4ccfa --- /dev/null +++ b/charts/espresso/values.yaml @@ -0,0 +1,281 @@ +global: + # -- Service account + # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + # + serviceAccount: + create: false + annotations: {} + + namespaceOverride: "" + + # -- Whether or not to allocate persistent volume disk for the data directory. + # In case of node failure, the node data directory will still persist. + # + persistence: + storageClassName: "" + accessModes: + - ReadWriteOnce + size: 150Gi + annotations: {} + + rbac: + create: true + + # -- Network type to be used in Espresso's Node Validator Dashboard for identity purposes. + # Values: Residential, Hosted, AWS, Azure, GCP, Cloud Provider. + # If the value is a well known cloud provider, then availability zone could be added. E.g. AWS us-east-1a. + networkType: "" + +nodes: + normal: + replicaCount: 1 + command: + - "sequencer" + - "--" + - "http" + - "--" + - "catchup" + - "--" + - "status" + sqlStorage: false + volumeMount: true + resources: + requests: + memory: "12000Mi" + # -- Secrets to be used by the node. + # The secret key must be the same as the one in the Secret resource or ExternalSecret resource. + secrets: + # -- Sequencer secret key. Must match the secret key in the Secret resource or ExternalSecret resource for the node type. + sequencerSecretKey: "" + data: [] + # secrets: + # data: + # - secretKey: espresso-sequencer-private-keys-normal-asia + # remoteRef: + # key: "espresso-sequencer-private-keys-normal-asia" + # -- Node name to identify the node in the Espresso's Node Validator Dashboard. The node index will be appended to the node name. + nodeName: "" + # nodeName: "nethermind-sequencer-" + # da: + # replicaCount: 0 + # command: + # - "sequencer" + # - "--" + # - "storage-sql" + # - "--" + # - "http" + # - "--" + # - "catchup" + # - "--" + # - "status" + # - "--" + # - "query" + # sqlStorage: true + # volumeMount: false + # resources: + # requests: + # memory: "12000Mi" + # -- Secrets to be used by the node. + # The secret key must be the same as the one in the Secret resource or ExternalSecret resource. + # secrets: + # # -- Sequencer secret key. Must match the secret key in the Secret resource or ExternalSecret resource for the sequencer node secrets. + # sequencerSecretKey: "" + # # -- Postgres secret key. Must match the secret key in the Secret resource or ExternalSecret resource for the postgres related secrets. + # postgresSecretKey: "" + # data: [] + # # secrets: + # # data: + # # - secretKey: espresso-sequencer-private-keys-normal-asia + # # remoteRef: + # # key: "espresso-sequencer-private-keys-normal-asia" + # # -- Node name to identify the node in the Espresso's Node Validator Dashboard. The node index will be appended to the node name. + # nodeName: "" + # nodeName: "nethermind-sequencer-da-" + +nodes_config: + # Required parameters + ESPRESSO_SEQUENCER_L1_PROVIDER: "" # JSON-RPC endpoint for Sepolia testnet + ESPRESSO_SEQUENCER_ORCHESTRATOR_URL: https://orchestrator-7BEFB0C9FFC.decaf.testnet.espresso.network + ESPRESSO_SEQUENCER_CDN_ENDPOINT: "cdn.decaf.testnet.espresso.network:1737" + ESPRESSO_STATE_RELAY_SERVER_URL: https://state-relay.decaf.testnet.espresso.network + ESPRESSO_SEQUENCER_STATE_PEERS: https://query.decaf.testnet.espresso.network + ESPRESSO_SEQUENCER_GENESIS_FILE: "/genesis/decaf.toml" # Path to file containing genesis state + ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS: "0.0.0.0:31000" # The address to bind Libp2p to in host:port form. Other nodes should be able to access this. + ESPRESSO_SEQUENCER_LIBP2P_ADVERTISE_ADDRESS: "0.0.0.0:31000" # The address we should advertise to other nodes as being our Libp2p endpoint (in host:port form). It should resolve a connection to the above bind address. + # Optional parameters + RUST_LOG: "warn,libp2p=off" + RUST_LOG_FORMAT: "json" + ESPRESSO_SEQUENCER_STORAGE_PATH: "/mount/sequencer/store/" # Path in container to store consensus state + ESPRESSO_SEQUENCER_API_PORT: 80 # Port on which to host metrics and healthchecks + ESPRESSO_SEQUENCER_IDENTITY_COMPANY_NAME: "Nethermind" + ESPRESSO_SEQUENCER_IDENTITY_COMPANY_WEBSITE: "https://nethermind.io" + +image: + repository: ghcr.io/espressosystems/espresso-sequencer/sequencer + tag: main + pullPolicy: IfNotPresent + +# -- Init image is used to manage which secrets the pod should use. +initImage: + repository: "bitnami/kubectl" + tag: "1.28" + pullPolicy: IfNotPresent + +# -- Keystore-CLI settings. Used to manage keys on Secret Store. +keystoreCLI: + image: + repository: "nethermindeth/espresso-keystore-cli" + tag: "v0.1.1" + pullPolicy: IfNotPresent + projectId: "" # GCP Project ID + pv: + secretId: "" # GCP Secret ID + db: + secretId: "" # GCP Secret ID of the DB credentials + host: "" # DB host + user: "" # DB user + +service: + type: ClusterIP + # Port will target ESPRESSO_SEQUENCER_API_PORT + annotations: {} + +serviceMonitor: + # -- The namespace in which the ServiceMonitor will be created + namespace: "" + # -- The interval at which metrics should be scraped + # interval: 30s + # -- The timeout after which the scrape is ended + scrapeTimeout: "" + # -- Metrics RelabelConfigs to apply to samples before scraping. + relabellings: [] + # -- Metrics RelabelConfigs to apply to samples before ingestion. + metricRelabelings: [] + # -- Specify honorLabels parameter to add the scrape endpoint + honorLabels: false + # -- Additional labels that can be used so ServiceMonitor resource(s) can be discovered by Prometheus + additionalLabels: {} +# -- Custom PrometheusRule to be defined +# ref: https://github.com/coreos/prometheus-operator#customresourcedefinitions +# +prometheusRule: + # -- Create a default set of Alerts + default: true + # -- The namespace in which the prometheusRule will be created + namespace: "" + # -- Additional labels for the prometheusRule + additionalLabels: {} + # -- Custom Prometheus rules + rules: [] + +ingress: + enabled: false + className: "" + + # -- Route Prefix. Can skip it if any item of path has the path defined. + routePrefix: / + + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + labels: {} + + # -- Hostnames. + # Can be provided if Ingress is enabled. + hosts: [] + # hosts: + # - host: espresso.example.com + # -- Paths to use for ingress rules + # paths: + # - path: "/metrics" + # port: 9000 + # pathType: "Prefix" + # - path: "/api" + # port: 8080 + # pathType: "Exact" + # - path: "/health" + # port: 8085 + # pathType: "Prefix" + # serviceName: "alternativeServiceName" + # - paths: + # - path: "/metrics" + # port: 9000 + # pathType: "Prefix" + # By default, the Service created by this chart is used as the target + # Service for the Ingress. + # If not defined the following default object will be used: + # hosts: + # paths: + # - path: "/" + # port: 8000 + # pathType: "ImplementationSpecific" + # serviceName: "" + + + # -- TLS configuration for Ingress + # Secret must be manually created in the namespace + # + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +externalSecrets: + enabled: false + name: "eso-espresso-sequencer-secrets" + secretStoreRef: + name: secretStoreRef + kind: SecretStore + +# -- Provide a name to substitute for the full names of resources +fullnameOverride: "" + +# -- Service account +# ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +# +serviceAccount: + # -- Annotations to add to the service account + annotations: {} + # -- The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + # + name: "" + +rbac: + # -- The name of the role to use. + # If not set and create is true, a name is generated using the fullname template + # + name: "" + create: true + # -- Required ClusterRole rules + clusterRules: + # # -- Required to create ServiceMonitor CRD + # # + - apiGroups: [""] + resources: + - "services" + - "endpoints" + verbs: + - "get" + - "list" + - "watch" + # -- Required Role rules + rules: + # -- Required to create Sequencer env Secret. + # + - apiGroups: [""] + resources: + - "secrets" + verbs: + - "create" + - "get" + - "list" + - "watch" + - "delete" + - apiGroups: [""] + resources: + - "services" + verbs: + - "get" + - "list" + - "watch"