diff --git a/deploy/charts/backend/Chart.yaml b/deploy/charts/backend/Chart.yaml new file mode 100644 index 00000000..b2fd66b4 --- /dev/null +++ b/deploy/charts/backend/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v2 +name: sh-mspbackend +description: A Helm chart for StorageHub MSP Backend API +type: application +version: 0.1.0 +appVersion: "1.0.0" +maintainers: +- name: StorageHub Team + email: team@storagehub.io \ No newline at end of file diff --git a/deploy/charts/backend/README.md b/deploy/charts/backend/README.md new file mode 100644 index 00000000..efedff2d --- /dev/null +++ b/deploy/charts/backend/README.md @@ -0,0 +1,270 @@ +# StorageHub MSP Backend Helm Chart + +This Helm chart deploys the StorageHub MSP Backend API service that provides REST API access to the StorageHub network data. + +## Overview + +The StorageHub MSP Backend API: +- Connects to a StorageHub Indexer's database for indexed blockchain data +- Connects to a StorageHub MSP node for real-time blockchain queries +- Provides REST API endpoints for StorageHub operations + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ +- Running StorageHub Indexer database (PostgreSQL) +- Running StorageHub MSP node + +## Installation + +### Using base configuration + +```bash +helm install sh-mspbackend ./charts/backend \ + -f ./charts/backend/storagehub/sh-mspbackend.yaml +``` + +### For Local environment + +```bash +helm install sh-mspbackend ./charts/backend \ + -f ./charts/backend/storagehub/sh-mspbackend.yaml \ + -f ./environments/local/sh-mspbackend.yaml \ + -n kt-datahaven-local +``` + +### For Stagenet environment + +```bash +helm install sh-mspbackend ./charts/backend \ + -f ./charts/backend/storagehub/sh-mspbackend.yaml \ + -f ./environments/stagenet/sh-mspbackend.yaml \ + -n datahaven-stagenet +``` + +## Configuration + +### Key Parameters + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `image.repository` | Container image repository | `moonsonglabs/storage-hub-msp-backend` | +| `image.tag` | Container image tag | `latest` | +| `replicaCount` | Number of replicas | `1` | +| `service.type` | Kubernetes service type | `ClusterIP` | +| `service.port` | Service port | `8080` | +| `service.targetPort` | Service target port | `80` | +| `backend.port` | Backend application port | `8080` | +| `backend.database.url` | PostgreSQL connection URL | `postgresql://storagehub:storagehub@sh-indexer-db:5432/storagehub` | +| `backend.rpc.endpoint` | WebSocket RPC endpoint | `ws://sh-idxnode:9944` | +| `backend.api.defaultPageSize` | Default page size for API results | `20` | +| `backend.api.maxPageSize` | Maximum page size for API results | `100` | +| `ingress.enabled` | Enable ingress | `false` | + +### Configuration File + +The backend uses a TOML configuration file passed via the `--config` CLI argument. This file is automatically generated from the Helm values and mounted as a ConfigMap at `/configs/config.toml`. + +#### Basic Configuration: +```yaml +backend: + port: 8080 + + database: + url: postgresql://indexer:indexer@sh-idxnode-db-postgresql:5432/datahaven + + rpc: + endpoint: ws://sh-mspnode-0:9955 + + api: + defaultPageSize: 20 + maxPageSize: 100 + + auth: + jwtSecret: "your-secret-here" + + args: + - "--config" + - "/configs/config.toml" + +configMap: + enabled: true +``` + +#### Alternative: Building Database URL from Components +The chart can also construct the database URL from separate components: +```yaml +backend: + database: + host: sh-idxnode-db-postgresql + port: 5432 + name: datahaven + user: indexer + password: production_password +``` + +**Note:** For production deployments, consider using Kubernetes Secrets or external secret management solutions for sensitive values like database passwords and JWT secrets. + +### Environment Variables + +Additional environment variables can be configured: + +```yaml +backend: + env: + NODE_ENV: production + LOG_LEVEL: info +``` + +### Additional ConfigMap Data + +You can add extra files to the ConfigMap: + +```yaml +configMap: + enabled: true + data: + custom-config.yaml: | + # Your custom configuration here + key: value +``` + +### CLI Arguments + +Additional CLI arguments can be specified to pass to the backend application: + +```yaml +backend: + args: + - "--config" + - "/configs/config.toml" + - "--log-level" + - "debug" +``` + +### Using Environment Variables from ConfigMaps or Secrets + +You can inject environment variables from existing ConfigMaps or Secrets: + +```yaml +backend: + envFrom: + - configMapRef: + name: my-config + - secretRef: + name: my-secret +``` + +## Accessing the Service + +### Local Environment + +When deployed with `NodePort` service type: +```bash +# Access via NodePort (configured as 30300 in local environment) +curl http://localhost:30300/ + +# Or via ingress if enabled +curl http://sh-mspbackend.datahaven.local/ +``` + +### Stagenet Environment + +```bash +# Access via ingress +curl https://sh-mspbackend.datahaven-kt.xyz/ +``` + +## Generated Configuration + +The chart automatically generates a `config.toml` file with the following structure: + +```toml +host = "0.0.0.0" +port = 8080 + +[api] +default_page_size = 20 +max_page_size = 100 + +[storage_hub] +rpc_url = "ws://sh-mspnode-0:9955" +msp_callback_url = "http://sh-mspbackend:8080" +timeout_secs = 30 +max_concurrent_requests = 100 +verify_tls = true +mock_mode = false + +[auth] +jwt_secret = "your-secret-here" + +[database] +url = "postgresql://indexer:indexer@sh-idxnode-db-postgresql:5432/datahaven" +mock_mode = false +``` + +## Troubleshooting + +### Check pod status +```bash +kubectl get pods -l app.kubernetes.io/name=sh-mspbackend -n +``` + +### View logs +```bash +kubectl logs -l app.kubernetes.io/name=sh-mspbackend -n +``` + +### Verify database connection +For local environment: +```bash +kubectl exec -it deployment/sh-mspbackend -n kt-datahaven-local -- nc -zv sh-idxnode-db-postgresql 5432 +``` + +For stagenet environment: +```bash +kubectl exec -it deployment/sh-mspbackend -n datahaven-stagenet -- nc -zv sh-idxnode-db-postgresql 5432 +``` + +### Verify RPC connection +For local environment: +```bash +kubectl exec -it deployment/sh-mspbackend -n kt-datahaven-local -- nc -zv sh-mspnode-0 9955 +``` + +For stagenet environment: +```bash +kubectl exec -it deployment/sh-mspbackend -n datahaven-stagenet -- nc -zv sh-mspnode-0 9955 +``` + +### View generated configuration +```bash +kubectl get configmap sh-mspbackend-config -n -o yaml +``` + +## Uninstallation + +```bash +# For local environment +helm uninstall sh-mspbackend -n kt-datahaven-local + +# For stagenet environment +helm uninstall sh-mspbackend -n datahaven-stagenet +``` + +## Environment-Specific Examples + +### Local Environment Values +See `environments/local/sh-mspbackend.yaml` for the complete local configuration, which includes: +- NodePort service on port 30300 +- Debug logging +- Traefik ingress at `sh-mspbackend.datahaven.local` +- Minimal resource requests for development + +### Stagenet Environment Values +See `environments/stagenet/sh-mspbackend.yaml` for the complete stagenet configuration, which includes: +- ClusterIP service with AWS NLB annotations +- Production logging levels +- ALB ingress with SSL at `sh-mspbackend.datahaven-kt.xyz` +- Production-level resource requests and limits \ No newline at end of file diff --git a/deploy/charts/backend/storagehub/sh-mspbackend.yaml b/deploy/charts/backend/storagehub/sh-mspbackend.yaml new file mode 100644 index 00000000..8f8de5e0 --- /dev/null +++ b/deploy/charts/backend/storagehub/sh-mspbackend.yaml @@ -0,0 +1,64 @@ +# StorageHub MSP Backend API base configuration +# This file contains the base configuration for the StorageHub MSP Backend API + +# Chart metadata +fullnameOverride: sh-mspbackend + +# Container image +image: + repository: moonsonglabs/storage-hub-msp-backend + tag: latest + pullPolicy: Always + +# Service configuration +service: + type: ClusterIP + port: 8080 + targetPort: 80 + +# Backend API configuration +backend: + port: 8080 + + # Database connection to StorageHub Indexer PostgreSQL + database: + url: postgresql://indexer:indexer@sh-idxnode-db-postgresql:5432/datahaven + + # RPC connection to StorageHub Indexer node + rpc: + endpoint: ws://sh-mspnode-0:9955 + + # Authentication (set in environment-specific values) + # auth: + # jwtSecret: "set-in-environment-values" + + # CLI arguments for the backend application + args: + - "--config" + - "/configs/config.toml" + +# Resource limits +resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + +# Service account +serviceAccount: + create: true + name: sh-mspbackend + +# Security context +securityContext: + runAsNonRoot: true + runAsUser: 1000 + capabilities: + drop: + - ALL + +# Ingress configuration (disabled by default, enabled per environment) +ingress: + enabled: false diff --git a/deploy/charts/backend/templates/_helpers.tpl b/deploy/charts/backend/templates/_helpers.tpl new file mode 100644 index 00000000..d9930293 --- /dev/null +++ b/deploy/charts/backend/templates/_helpers.tpl @@ -0,0 +1,60 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "backend.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "backend.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "backend.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "backend.labels" -}} +helm.sh/chart: {{ include "backend.chart" . }} +{{ include "backend.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "backend.selectorLabels" -}} +app.kubernetes.io/name: {{ include "backend.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "backend.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "backend.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/deploy/charts/backend/templates/configmap.yaml b/deploy/charts/backend/templates/configmap.yaml new file mode 100644 index 00000000..22afaa07 --- /dev/null +++ b/deploy/charts/backend/templates/configmap.yaml @@ -0,0 +1,48 @@ +{{- if .Values.configMap.enabled -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "backend.fullname" . }}-config + labels: + {{- include "backend.labels" . | nindent 4 }} +data: + config.toml: | + # StorageHub Backend Configuration + host = "0.0.0.0" + port = {{ .Values.backend.port }} + + [api] + default_page_size = {{ .Values.backend.api.defaultPageSize | default 20 }} + max_page_size = {{ .Values.backend.api.maxPageSize | default 100 }} + + [storage_hub] + {{- if .Values.backend.rpc.endpoint }} + rpc_url = {{ .Values.backend.rpc.endpoint | quote }} + {{- end }} + + msp_callback_url = "http://{{ include "backend.fullname" . }}:8080" + + timeout_secs = 30 + max_concurrent_requests = 100 + verify_tls = true + mock_mode = false + + [auth] + {{- if .Values.backend.auth.jwtSecret }} + jwt_secret = {{ .Values.backend.auth.jwtSecret | quote }} + {{- end }} + + [database] + {{- if .Values.backend.database.url }} + url = {{ .Values.backend.database.url | quote }} + {{- else if .Values.backend.database.host }} + url = "postgresql://{{ .Values.backend.database.user | default "indexer" }}:{{ .Values.backend.database.password | default "password" }}@{{ .Values.backend.database.host }}:{{ .Values.backend.database.port | default 5432 }}/{{ .Values.backend.database.name | default "storagehub" }}" + {{- else }} + url = "postgresql://indexer:password@sh-indexer-db:5432/storagehub" + {{- end }} + + mock_mode = false + {{- range $key, $value := .Values.configMap.data }} + {{ $key }}: {{ $value | quote }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/deploy/charts/backend/templates/deployment.yaml b/deploy/charts/backend/templates/deployment.yaml new file mode 100644 index 00000000..c998e2eb --- /dev/null +++ b/deploy/charts/backend/templates/deployment.yaml @@ -0,0 +1,80 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "backend.fullname" . }} + labels: + {{- include "backend.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "backend.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "backend.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "backend.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.backend.port }} + protocol: TCP + env: + - name: PORT + value: {{ .Values.backend.port | quote }} + {{- range $key, $value := .Values.backend.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- with .Values.backend.envFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.backend.args }} + args: + {{- toYaml .Values.backend.args | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.configMap.enabled }} + volumeMounts: + - name: config + mountPath: "/configs/config.toml" + subPath: "config.toml" + readOnly: true + {{- end }} + {{- if .Values.configMap.enabled }} + volumes: + - name: config + configMap: + name: {{ include "backend.fullname" . }}-config + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} \ No newline at end of file diff --git a/deploy/charts/backend/templates/ingress.yaml b/deploy/charts/backend/templates/ingress.yaml new file mode 100644 index 00000000..ca8849a5 --- /dev/null +++ b/deploy/charts/backend/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "backend.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "backend.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/deploy/charts/backend/templates/secret.yaml b/deploy/charts/backend/templates/secret.yaml new file mode 100644 index 00000000..812e46ef --- /dev/null +++ b/deploy/charts/backend/templates/secret.yaml @@ -0,0 +1,13 @@ +{{- if .Values.secrets.enabled -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "backend.fullname" . }}-custom + labels: + {{- include "backend.labels" . | nindent 4 }} +type: Opaque +data: + {{- range $key, $value := .Values.secrets.data }} + {{ $key }}: {{ $value | b64enc | quote }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/deploy/charts/backend/templates/service.yaml b/deploy/charts/backend/templates/service.yaml new file mode 100644 index 00000000..8d65dcbb --- /dev/null +++ b/deploy/charts/backend/templates/service.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "backend.fullname" . }} + labels: + {{- include "backend.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + {{- if and (eq .Values.service.type "NodePort") .Values.service.nodePort }} + nodePort: {{ .Values.service.nodePort }} + {{- end }} + selector: + {{- include "backend.selectorLabels" . | nindent 4 }} \ No newline at end of file diff --git a/deploy/charts/backend/templates/serviceaccount.yaml b/deploy/charts/backend/templates/serviceaccount.yaml new file mode 100644 index 00000000..56355387 --- /dev/null +++ b/deploy/charts/backend/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "backend.serviceAccountName" . }} + labels: + {{- include "backend.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/deploy/charts/backend/values.yaml b/deploy/charts/backend/values.yaml new file mode 100644 index 00000000..61ff0885 --- /dev/null +++ b/deploy/charts/backend/values.yaml @@ -0,0 +1,122 @@ +# Default values for backend. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: storagehub/backend + pullPolicy: IfNotPresent + tag: "latest" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + create: true + annotations: {} + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 3000 + targetPort: 3000 + annotations: {} + +ingress: + enabled: false + className: "" + annotations: {} + hosts: + - host: api.storagehub.local + paths: + - path: / + pathType: Prefix + tls: [] + +resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# Backend API Configuration +backend: + # Port the backend listens on + port: 3000 + + # Database configuration + database: + # Full database connection URL + url: "postgresql://storagehub:storagehub@sh-indexer-db:5432/storagehub" + # Use existing secret for database URL + existingSecret: "" + # Key in the secret containing the DATABASE_URL + existingSecretUrlKey: "database-url" + + # RPC Node configuration + rpc: + endpoint: "ws://sh-idxnode:9944" + # Alternative HTTP endpoint if needed + httpEndpoint: "http://sh-idxnode:9933" + + # API configuration + api: + defaultPageSize: 20 + maxPageSize: 100 + + # Authentication configuration + auth: + jwtSecret: "" + + # Environment variables + env: + NODE_ENV: "production" + LOG_LEVEL: "info" + + # Additional environment variables from ConfigMap or Secret + envFrom: [] + + # CLI arguments to pass to the backend application + args: [] + # Example: + # args: + # - "--config" + # - "/app/config.toml" + # - "--log-level" + # - "debug" + +# ConfigMap for configuration files +configMap: + enabled: true + # Additional configuration to merge into config.json + extraConfig: {} + # Additional files to add to ConfigMap + data: {} + +# Secrets for sensitive data +secrets: + enabled: false + data: {} \ No newline at end of file diff --git a/deploy/charts/node/datahaven/dh-bootnode.yaml b/deploy/charts/node/datahaven/dh-bootnode.yaml index 4d537199..3da23587 100644 --- a/deploy/charts/node/datahaven/dh-bootnode.yaml +++ b/deploy/charts/node/datahaven/dh-bootnode.yaml @@ -26,6 +26,7 @@ node: - "--allow-private-ipv4" - "--discover-local" - "--network-backend libp2p" + - "--pool-type fork-aware" ingress: enabled: false diff --git a/deploy/charts/node/datahaven/dh-validator.yaml b/deploy/charts/node/datahaven/dh-validator.yaml index 9822a469..dc9a2356 100644 --- a/deploy/charts/node/datahaven/dh-validator.yaml +++ b/deploy/charts/node/datahaven/dh-validator.yaml @@ -43,6 +43,7 @@ node: persistGeneratedNodeKey: true flags: - "--network-backend libp2p" + - "--pool-type fork-aware" # Note: Bootnode discovery will happen automatically via the chainspec downloaded from customChainspecUrl enableOffchainIndexing: true diff --git a/deploy/charts/node/storagehub/sh-bspnode.yaml b/deploy/charts/node/storagehub/sh-bspnode.yaml new file mode 100644 index 00000000..56697db8 --- /dev/null +++ b/deploy/charts/node/storagehub/sh-bspnode.yaml @@ -0,0 +1,47 @@ +name: sh-bspnode +description: Datahaven BSP node +fullnameOverride: sh-bspnode + +image: + repository: datahavenxyz/datahaven + tag: main + pullPolicy: Always + +imagePullSecrets: + - name: datahaven-dockerhub + +node: + command: datahaven-node + customChainspecUrl: http://dh-bootnode:8080/chainspec.json + forceDownloadChainspec: true + role: full + replicas: 2 + chainData: + pruning: 1000 + storageClass: "gp2" + chainKeystore: + storageClass: "gp2" + keys: + # This is Alice seed. To generate new seed run: docker run --rm datahavenxyz/datahaven:latest key generate + - seed: "bottom drive obey lake curtain smoke basket hold race lonely fit walk" + type: bcsv + scheme: ecdsa + # ${HOSTNAME##*-} will be evaluated as the pod index, pod-0: //Eve, pod-1: //Ferdie + extraDerivation: '$([ "${HOSTNAME##*-}" = "0" ] && echo "//Eve" || echo "//Ferdie")' + persistGeneratedNodeKey: true + flags: + - "--allow-private-ipv4" + - "--discover-local" + - "--network-backend libp2p" + - "--provider" + - "--provider-type bsp" + - "--max-storage-capacity 10737418240" # 10 GiB + - "--jump-capacity=1073741824" # 1 GiB + +ingress: + enabled: false + perReplica: true + wildcardDomain: datahaven.local + # If enabled, this would generate: + # - sh-bspnode-0.datahaven.local + # - sh-bspnode-1.datahaven.local diff --git a/deploy/charts/node/storagehub/sh-fisherman.yaml b/deploy/charts/node/storagehub/sh-fisherman.yaml new file mode 100644 index 00000000..612584fd --- /dev/null +++ b/deploy/charts/node/storagehub/sh-fisherman.yaml @@ -0,0 +1,44 @@ +name: sh-fisherman +description: Datahaven Fisherman node +fullnameOverride: sh-fisherman + +image: + repository: datahavenxyz/datahaven + tag: main + pullPolicy: Always + +imagePullSecrets: + - name: datahaven-dockerhub + +node: + command: datahaven-node + customChainspecUrl: http://dh-bootnode:8080/chainspec.json + forceDownloadChainspec: true + role: full + replicas: 1 + chainData: + pruning: 1000 + storageClass: "gp2" + chainKeystore: + storageClass: "gp2" + keys: + # This is Alice seed. To generate new seed run: docker run --rm datahavenxyz/datahaven:latest key generate + - seed: "bottom drive obey lake curtain smoke basket hold race lonely fit walk" + type: bcsv + scheme: ecdsa + # ${HOSTNAME##*-} will be evaluated as the pod index, pod-0: //Gustavo, pod-1: //Hermano + extraDerivation: '$([ "${HOSTNAME##*-}" = "0" ] && echo "//Gustavo" || echo "//Hermano")' + persistGeneratedNodeKey: true + flags: + - "--allow-private-ipv4" + - "--discover-local" + - "--network-backend libp2p" + - "--fisherman" + - "--fisherman-database-url postgresql://indexer:indexer@sh-idxnode-db-postgresql:5432/datahaven" + +ingress: + enabled: false + perReplica: false + wildcardDomain: datahaven.local + # If enabled, this would generate: + # - sh-fisherman-0.datahaven.local diff --git a/deploy/charts/node/storagehub/sh-idxnode.yaml b/deploy/charts/node/storagehub/sh-idxnode.yaml new file mode 100644 index 00000000..8a606ab5 --- /dev/null +++ b/deploy/charts/node/storagehub/sh-idxnode.yaml @@ -0,0 +1,38 @@ +name: sh-idxnode +description: Datahaven Indexer node +fullnameOverride: sh-idxnode + +image: + repository: datahavenxyz/datahaven + tag: main + pullPolicy: Always + +imagePullSecrets: + - name: datahaven-dockerhub + +node: + command: datahaven-node + customChainspecUrl: http://dh-bootnode:8080/chainspec.json + forceDownloadChainspec: true + role: full + replicas: 1 + chainData: + pruning: 1000 + storageClass: "gp2" + chainKeystore: + storageClass: "gp2" + persistGeneratedNodeKey: true + flags: + - "--allow-private-ipv4" + - "--discover-local" + - "--network-backend libp2p" + - "--indexer" + - "--indexer-mode full" + - "--indexer-database-url postgresql://indexer:indexer@sh-idxnode-db-postgresql:5432/datahaven" + +ingress: + enabled: false + perReplica: false + wildcardDomain: datahaven.local + # If enabled, this would generate: + # - sh-idxnode-0.datahaven.local diff --git a/deploy/charts/node/storagehub/sh-mspnode.yaml b/deploy/charts/node/storagehub/sh-mspnode.yaml new file mode 100644 index 00000000..34f6de6a --- /dev/null +++ b/deploy/charts/node/storagehub/sh-mspnode.yaml @@ -0,0 +1,49 @@ +name: sh-mspnode +description: Datahaven MSP node +fullnameOverride: sh-mspnode + +image: + repository: datahavenxyz/datahaven + tag: main + pullPolicy: Always + +imagePullSecrets: + - name: datahaven-dockerhub + +node: + command: datahaven-node + customChainspecUrl: http://dh-bootnode:8080/chainspec.json + forceDownloadChainspec: true + role: full + replicas: 2 + chainData: + pruning: 1000 + storageClass: "gp2" + chainKeystore: + storageClass: "gp2" + keys: + # This is Alice seed. To generate new seed run: docker run --rm datahavenxyz/datahaven:latest key generate + - seed: "bottom drive obey lake curtain smoke basket hold race lonely fit walk" + type: bcsv + scheme: ecdsa + # ${HOSTNAME##*-} will be evaluated as the pod index, pod-0: //Charlie, pod-1: //Dave + extraDerivation: '$([ "${HOSTNAME##*-}" = "0" ] && echo "//Charlie" || echo "//Dave")' + persistGeneratedNodeKey: true + allowUnsafeRpcMethods: true + flags: + - "--allow-private-ipv4" + - "--discover-local" + - "--network-backend libp2p" + - "--provider" + - "--provider-type msp" + - "--msp-charging-period 100" # in blocks, 100 blocks = 10 minutes + - "--max-storage-capacity 10737418240" # 10 GiB + - "--jump-capacity=1073741824" # 1 GiB + +ingress: + enabled: false + perReplica: true + wildcardDomain: datahaven.local + # If enabled, this would generate: + # - sh-mspnode-0.datahaven.local + # - sh-mspnode-1.datahaven.local diff --git a/deploy/environments/local/dh-bootnode.yaml b/deploy/environments/local/dh-bootnode.yaml index de46dd2e..245db825 100644 --- a/deploy/environments/local/dh-bootnode.yaml +++ b/deploy/environments/local/dh-bootnode.yaml @@ -15,7 +15,7 @@ imagePullSecrets: ingress: enabled: false ingressClassName: traefik - host: dh-bootnode-0.datahaven-kt.local + host: dh-bootnode-0.datahaven.local node: chain: local diff --git a/deploy/environments/local/sh-bspnode.yaml b/deploy/environments/local/sh-bspnode.yaml new file mode 100644 index 00000000..db3339f3 --- /dev/null +++ b/deploy/environments/local/sh-bspnode.yaml @@ -0,0 +1,44 @@ +# BSP-specific settings for Local environment + +global: + environment: local + namespace: kt-datahaven-local + +image: + tag: local + pullPolicy: IfNotPresent + +imagePullSecrets: + - name: datahaven-dockerhub + +# Ingress disabled for local (can be enabled for testing) +ingress: + enabled: false + ingressClassName: traefik + perReplica: true + wildcardDomain: datahaven.local + # If enabled, this would generate: + # - sh-bspnode-0.datahaven.local + # - sh-bspnode-1.datahaven.local + +node: + chain: local + customChainspecUrl: + forceDownloadChainspec: false + chainData: + storageClass: "hostpath" + persistence: + size: 10Gi + chainKeystore: + storageClass: "hostpath" + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "200m" + perNodeServices: + apiService: + enabled: true + type: NodePort diff --git a/deploy/environments/local/sh-fisherman.yaml b/deploy/environments/local/sh-fisherman.yaml new file mode 100644 index 00000000..1f544a8d --- /dev/null +++ b/deploy/environments/local/sh-fisherman.yaml @@ -0,0 +1,44 @@ +# Fisherman-specific settings for Local environment + +global: + environment: local + namespace: kt-datahaven-local + +image: + tag: local + pullPolicy: IfNotPresent + +imagePullSecrets: + - name: datahaven-dockerhub + +# Ingress disabled for local (can be enabled for testing) +ingress: + enabled: false + ingressClassName: traefik + perReplica: true + wildcardDomain: datahaven.local + # If enabled, this would generate: + # - sh-fisherman-0.datahaven.local + # - sh-fisherman-1.datahaven.local + +node: + chain: local + customChainspecUrl: + forceDownloadChainspec: false + chainData: + storageClass: "hostpath" + persistence: + size: 10Gi + chainKeystore: + storageClass: "hostpath" + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "200m" + perNodeServices: + apiService: + enabled: true + type: NodePort diff --git a/deploy/environments/local/sh-idxnode-db.yaml b/deploy/environments/local/sh-idxnode-db.yaml new file mode 100644 index 00000000..c66998cd --- /dev/null +++ b/deploy/environments/local/sh-idxnode-db.yaml @@ -0,0 +1,21 @@ +# Install PostgreSQL into local k8S cluster +# helm repo add bitnami https://charts.bitnami.com/bitnami +# helm repo update +# helm install sh-idxnode-db bitnami/postgresql -f ./deploy/environments/local/sh-idxnode-db.yaml -n datahaven-local + +auth: + username: indexer + password: indexer + database: datahaven + postgresPassword: postgres +primary: + persistence: + enabled: true + size: 10Gi # requires a default StorageClass + resources: + requests: { cpu: "100m", memory: "256Mi" } + limits: { cpu: "500m", memory: "512Mi" } +volumePermissions: + enabled: true # helps with FS perms on some storage classes +metrics: + enabled: true # optional: Prometheus exporter diff --git a/deploy/environments/local/sh-idxnode.yaml b/deploy/environments/local/sh-idxnode.yaml new file mode 100644 index 00000000..a3aeda52 --- /dev/null +++ b/deploy/environments/local/sh-idxnode.yaml @@ -0,0 +1,33 @@ +# Indexer-specific settings for Local environment + +global: + environment: local + namespace: kt-datahaven-local + +image: + tag: local + pullPolicy: IfNotPresent + +imagePullSecrets: + - name: datahaven-dockerhub + +ingress: + enabled: false + +node: + chain: local + customChainspecUrl: + forceDownloadChainspec: false + chainData: + storageClass: "hostpath" + persistence: + size: 10Gi + chainKeystore: + storageClass: "hostpath" + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "200m" diff --git a/deploy/environments/local/sh-mspbackend.yaml b/deploy/environments/local/sh-mspbackend.yaml new file mode 100644 index 00000000..ae5006dc --- /dev/null +++ b/deploy/environments/local/sh-mspbackend.yaml @@ -0,0 +1,73 @@ +# StorageHub MSP Backend API configuration for Local environment + +global: + environment: local + namespace: kt-datahaven-local + +# Use local image +image: + repository: moonsonglabs/storage-hub-msp-backend + tag: latest + pullPolicy: Always + +imagePullSecrets: + - name: datahaven-dockerhub + +# Single replica for local development +replicaCount: 1 + +# Service configuration for local access +service: + type: NodePort + port: 8080 + nodePort: 30300 # Fixed NodePort for local access + +# Backend configuration for local environment +backend: + database: + url: postgresql://indexer:indexer@sh-idxnode-db-postgresql:5432/datahaven + + rpc: + url: ws://sh-idxnode:9944 + + auth: + jwtSecret: "local-development-secret" + + env: + LOG_LEVEL: debug + + # CLI arguments for local development + args: + - "--config" + - "/app/config/config.toml" + +# Minimal resources for local development +resources: + requests: + memory: "128Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "200m" + +# Enable ingress for local development with Traefik +ingress: + enabled: true + className: traefik + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: web + hosts: + - host: sh-mspbackend.datahaven.local + paths: + - path: / + pathType: Prefix + +# ConfigMap for local environment +configMap: + enabled: true + extraConfig: + environment: "local" + features: + debug: true + swagger: true + metrics: true \ No newline at end of file diff --git a/deploy/environments/local/sh-mspnode.yaml b/deploy/environments/local/sh-mspnode.yaml new file mode 100644 index 00000000..b4bdb88c --- /dev/null +++ b/deploy/environments/local/sh-mspnode.yaml @@ -0,0 +1,44 @@ +# MSP-specific settings for Local environment + +global: + environment: local + namespace: kt-datahaven-local + +image: + tag: local + pullPolicy: IfNotPresent + +imagePullSecrets: + - name: datahaven-dockerhub + +# Ingress disabled for local (can be enabled for testing) +ingress: + enabled: false + ingressClassName: traefik + perReplica: true + wildcardDomain: datahaven.local + # If enabled, this would generate: + # - sh-mspnode-0.datahaven.local + # - sh-mspnode-1.datahaven.local + +node: + chain: local + customChainspecUrl: + forceDownloadChainspec: false + chainData: + storageClass: "hostpath" + persistence: + size: 10Gi + chainKeystore: + storageClass: "hostpath" + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "200m" + perNodeServices: + apiService: + enabled: true + type: NodePort diff --git a/deploy/environments/stagenet/dh-bootnode.yaml b/deploy/environments/stagenet/dh-bootnode.yaml index d03787dd..b3f41d40 100644 --- a/deploy/environments/stagenet/dh-bootnode.yaml +++ b/deploy/environments/stagenet/dh-bootnode.yaml @@ -5,7 +5,7 @@ global: namespace: kt-datahaven-stagenet image: - tag: main + tag: latest pullPolicy: Always imagePullSecrets: diff --git a/deploy/environments/stagenet/dh-validator.yaml b/deploy/environments/stagenet/dh-validator.yaml index 5deca490..66af0a42 100644 --- a/deploy/environments/stagenet/dh-validator.yaml +++ b/deploy/environments/stagenet/dh-validator.yaml @@ -5,7 +5,7 @@ global: namespace: kt-datahaven-stagenet image: - tag: main + tag: latest pullPolicy: Always imagePullSecrets: diff --git a/deploy/environments/stagenet/sh-bspnode.yaml b/deploy/environments/stagenet/sh-bspnode.yaml new file mode 100644 index 00000000..fa0ef078 --- /dev/null +++ b/deploy/environments/stagenet/sh-bspnode.yaml @@ -0,0 +1,51 @@ +# BSP-specific settings for stagenet + +global: + environment: stagenet + namespace: kt-datahaven-stagenet + +image: + tag: latest + pullPolicy: Always + +imagePullSecrets: + - name: datahaven-dockerhub + +ingress: + enabled: true + perReplica: true + wildcardDomain: datahaven-kt.xyz + # This will generate: + # - sh-bspnode-0.datahaven-kt.xyz + # - sh-bspnode-1.datahaven-kt.xyz + # (based on replica count) + annotations: + kubernetes.io/ingress.class: alb + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' + alb.ingress.kubernetes.io/load-balancer-name: sh-bspnode + alb.ingress.kubernetes.io/target-type: ip + alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS-1-2-2017-01 + alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=120 + alb.ingress.kubernetes.io/target-group-attributes: deregistration_delay.timeout_seconds=30 + alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:601766312530:certificate/61c2fc57-4ce4-4a80-90b8-2810c1221f09 + external-dns.alpha.kubernetes.io/hostname: sh-bspnode.datahaven-kt.xyz + meta.helm.sh/release-name: sh-bspnode + meta.helm.sh/release-namespace: kt-datahaven-stagenet + +node: + chain: stagenet-local + chainData: + persistence: + size: 20Gi + resources: + requests: + memory: "512Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "500m" + perNodeServices: + apiService: + enabled: true + type: NodePort diff --git a/deploy/environments/stagenet/sh-fisherman.yaml b/deploy/environments/stagenet/sh-fisherman.yaml new file mode 100644 index 00000000..2d82b49e --- /dev/null +++ b/deploy/environments/stagenet/sh-fisherman.yaml @@ -0,0 +1,51 @@ +# Fisherman-specific settings for stagenet + +global: + environment: stagenet + namespace: kt-datahaven-stagenet + +image: + tag: latest + pullPolicy: Always + +imagePullSecrets: + - name: datahaven-dockerhub + +ingress: + enabled: true + perReplica: true + wildcardDomain: datahaven-kt.xyz + # This will generate: + # - sh-fisherman-0.datahaven-kt.xyz + # - sh-fisherman-1.datahaven-kt.xyz + # (based on replica count) + annotations: + kubernetes.io/ingress.class: alb + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' + alb.ingress.kubernetes.io/load-balancer-name: sh-fisherman + alb.ingress.kubernetes.io/target-type: ip + alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS-1-2-2017-01 + alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=120 + alb.ingress.kubernetes.io/target-group-attributes: deregistration_delay.timeout_seconds=30 + alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:601766312530:certificate/61c2fc57-4ce4-4a80-90b8-2810c1221f09 + external-dns.alpha.kubernetes.io/hostname: sh-fisherman.datahaven-kt.xyz + meta.helm.sh/release-name: sh-fisherman + meta.helm.sh/release-namespace: kt-datahaven-stagenet + +node: + chain: stagenet-local + chainData: + persistence: + size: 20Gi + resources: + requests: + memory: "512Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "500m" + perNodeServices: + apiService: + enabled: true + type: NodePort diff --git a/deploy/environments/stagenet/sh-idxnode-db.yaml b/deploy/environments/stagenet/sh-idxnode-db.yaml new file mode 100644 index 00000000..1845defa --- /dev/null +++ b/deploy/environments/stagenet/sh-idxnode-db.yaml @@ -0,0 +1,22 @@ +# Install PostgreSQL into local k8S cluster +# helm repo add bitnami https://charts.bitnami.com/bitnami +# helm repo update +# helm install sh-idxnode-db bitnami/postgresql -f ./deploy/environments/local/sh-idxnode-db.yaml -n kt-datahaven-stagenet + +auth: + username: indexer + password: indexer + database: datahaven + postgresPassword: postgres +primary: + persistence: + enabled: true + size: 10Gi # requires a default StorageClass + storageClass: "gp2" + resources: + requests: { cpu: "100m", memory: "256Mi" } + limits: { cpu: "500m", memory: "512Mi" } +volumePermissions: + enabled: true # helps with FS perms on some storage classes +metrics: + enabled: true # optional: Prometheus exporter diff --git a/deploy/environments/stagenet/sh-idxnode.yaml b/deploy/environments/stagenet/sh-idxnode.yaml new file mode 100644 index 00000000..1e713ea2 --- /dev/null +++ b/deploy/environments/stagenet/sh-idxnode.yaml @@ -0,0 +1,28 @@ +# Indexer-specific settings for stagenet + +global: + environment: stagenet + namespace: kt-datahaven-stagenet + +image: + tag: latest + pullPolicy: Always + +imagePullSecrets: + - name: datahaven-dockerhub + +ingress: + enabled: false + +node: + chain: stagenet-local + chainData: + persistence: + size: 20Gi + resources: + requests: + memory: "512Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "500m" diff --git a/deploy/environments/stagenet/sh-mspbackend.yaml b/deploy/environments/stagenet/sh-mspbackend.yaml new file mode 100644 index 00000000..cf93b1d7 --- /dev/null +++ b/deploy/environments/stagenet/sh-mspbackend.yaml @@ -0,0 +1,71 @@ +# StorageHub MSP Backend API configuration for Stagenet environment + +global: + environment: stagenet + namespace: datahaven-stagenet + +# Stagenet image configuration +image: + repository: moonsonglabs/storage-hub-msp-backend + tag: latest + pullPolicy: Always + +imagePullSecrets: + - name: datahaven-dockerhub + +replicaCount: 1 + +# Service configuration +service: + type: ClusterIP + port: 8080 + annotations: + service.beta.kubernetes.io/aws-load-balancer-type: "nlb" + +# Backend configuration for stagenet +backend: + database: + url: postgresql://indexer:indexer@sh-idxnode-db-postgresql:5432/datahaven + + rpc: + endpoint: ws://sh-mspnode-0:9955 + + auth: + jwtSecret: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" + + # CLI arguments for production + args: + - "--config" + - "/configs/config.toml" + +# Production-ready resources +resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "1000m" + +# Enable ingress with SSL +ingress: + enabled: true + host: sh-mspbackend.datahaven-kt.xyz + annotations: + kubernetes.io/ingress.class: alb + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' + alb.ingress.kubernetes.io/load-balancer-name: sh-mspbackend + alb.ingress.kubernetes.io/target-type: ip + alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS-1-2-2017-01 + alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=120 + alb.ingress.kubernetes.io/target-group-attributes: deregistration_delay.timeout_seconds=30 + alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:601766312530:certificate/61c2fc57-4ce4-4a80-90b8-2810c1221f09 + external-dns.alpha.kubernetes.io/hostname: sh-mspbackend.datahaven-kt.xyz + meta.helm.sh/release-name: sh-mspbackend + meta.helm.sh/release-namespace: kt-datahaven-stagenet + hosts: + - host: sh-mspbackend.datahaven-kt.xyz + paths: + - path: / + pathType: Prefix diff --git a/deploy/environments/stagenet/sh-mspnode.yaml b/deploy/environments/stagenet/sh-mspnode.yaml new file mode 100644 index 00000000..6bf28aee --- /dev/null +++ b/deploy/environments/stagenet/sh-mspnode.yaml @@ -0,0 +1,51 @@ +# MSP-specific settings for stagenet + +global: + environment: stagenet + namespace: kt-datahaven-stagenet + +image: + tag: latest + pullPolicy: Always + +imagePullSecrets: + - name: datahaven-dockerhub + +ingress: + enabled: true + perReplica: true + wildcardDomain: datahaven-kt.xyz + # This will generate: + # - sh-mspnode-0.datahaven-kt.xyz + # - sh-mspnode-1.datahaven-kt.xyz + # (based on replica count) + annotations: + kubernetes.io/ingress.class: alb + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' + alb.ingress.kubernetes.io/load-balancer-name: sh-mspnode + alb.ingress.kubernetes.io/target-type: ip + alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS-1-2-2017-01 + alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=120 + alb.ingress.kubernetes.io/target-group-attributes: deregistration_delay.timeout_seconds=30 + alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:601766312530:certificate/61c2fc57-4ce4-4a80-90b8-2810c1221f09 + external-dns.alpha.kubernetes.io/hostname: sh-mspnode.datahaven-kt.xyz + meta.helm.sh/release-name: sh-mspnode + meta.helm.sh/release-namespace: kt-datahaven-stagenet + +node: + chain: stagenet-local + chainData: + persistence: + size: 20Gi + resources: + requests: + memory: "512Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "500m" + perNodeServices: + apiService: + enabled: true + type: NodePort diff --git a/operator/Dockerfile.local b/operator/Dockerfile.local new file mode 100644 index 00000000..f1f226b9 --- /dev/null +++ b/operator/Dockerfile.local @@ -0,0 +1,25 @@ +# Dockerfile.local - Fast local image build using pre-built binary +# Usage: docker build -f Dockerfile.local -t datahaven:local . +# Prerequisite: cargo build --release (or --release --features fast-runtime) + +FROM docker.io/parity/base-bin:latest + +# Copy the pre-built binary from local target directory +COPY ./target/release/datahaven-node /usr/local/bin/datahaven-node + +# Make sure binary is executable +RUN chmod +x /usr/local/bin/datahaven-node + +USER root +RUN useradd -m -u 1001 -U -s /bin/sh -d /datahaven datahaven && \ + mkdir -p /data /datahaven/.local/share && \ + chown -R datahaven:datahaven /data && \ + ln -s /data /datahaven/.local/share/datahaven && \ + /usr/local/bin/datahaven-node --version + +USER datahaven + +EXPOSE 30333 9933 9944 9615 +VOLUME ["/data"] + +ENTRYPOINT ["/usr/local/bin/datahaven-node"] diff --git a/test/cli/handlers/deploy/index.ts b/test/cli/handlers/deploy/index.ts index c6fea896..ca88ce32 100644 --- a/test/cli/handlers/deploy/index.ts +++ b/test/cli/handlers/deploy/index.ts @@ -9,6 +9,7 @@ import { deployDataHavenSolochain } from "./datahaven"; import { deployKurtosis } from "./kurtosis"; import { setParametersFromCollection } from "./parameters"; import { deployRelayers } from "./relayer"; +import { deployStorageHubComponents } from "./storagehub"; import { performValidatorOperations } from "./validator"; // Non-optional properties determined by having default values @@ -39,6 +40,7 @@ export interface DeployOptions { skipValidatorOperations: boolean; skipSetParameters: boolean; skipRelayers: boolean; + skipStorageHub: boolean; } const deployFunction = async (options: DeployOptions, launchedNetwork: LaunchedNetwork) => { @@ -82,6 +84,8 @@ const deployFunction = async (options: DeployOptions, launchedNetwork: LaunchedN await deployRelayers(options, launchedNetwork); + await deployStorageHubComponents(options, launchedNetwork); + // Cleaning up the port forwarding for the validator. await validatorPortForwardCleanup(); diff --git a/test/cli/handlers/deploy/storagehub.ts b/test/cli/handlers/deploy/storagehub.ts new file mode 100644 index 00000000..9422268d --- /dev/null +++ b/test/cli/handlers/deploy/storagehub.ts @@ -0,0 +1,216 @@ +import path from "node:path"; +import { $ } from "bun"; +import invariant from "tiny-invariant"; +import { logger, printDivider, printHeader } from "utils"; +import { waitFor } from "utils/waits"; +import { isNetworkReady } from "../../../launcher/datahaven"; +import type { LaunchedNetwork } from "../../../launcher/types/launchedNetwork"; +import { forwardPort } from "../common/kubernetes"; +import type { DeployOptions } from "."; + +/** + * Deploys StorageHub components (MSP, BSP, Indexer, Fisherman nodes and databases) in a Kubernetes namespace. + * + * @param options - Configuration options for launching the network. + * @param launchedNetwork - An instance of LaunchedNetwork to track the network's state. + * @returns A promise that resolves when all StorageHub components are deployed. + */ +export const deployStorageHubComponents = async ( + options: DeployOptions, + launchedNetwork: LaunchedNetwork +): Promise => { + if (options.skipStorageHub) { + logger.info("🏳️ Skipping StorageHub components deployment"); + printDivider(); + return; + } + + printHeader("Deploying StorageHub Components"); + + invariant(options.datahavenImageTag, "❌ DataHaven image tag not defined"); + + if (!options.dockerUsername) { + await checkTagExists(options.datahavenImageTag); + } + + // Deploy StorageHub Indexer database first (Indexer PostgreSQL database) + await deployStorageHubDatabase(options, launchedNetwork); + + // Deploy StorageHub nodes (MSP, BSP, Indexer, Fisherman) + await deployStorageHubNodes(options, launchedNetwork); + + // Deploy StorageHub MSP Backend API + await deployStorageHubBackend(options, launchedNetwork); + + await registerStorageHubNodes(launchedNetwork); + + printDivider(); +}; + +/** + * Deploys StorageHub PostgreSQL databases for Indexer and Fisherman nodes. + */ +const deployStorageHubDatabase = async ( + options: DeployOptions, + launchedNetwork: LaunchedNetwork +): Promise => { + logger.info("🗄️ Deploying StorageHub PostgreSQL database..."); + + const deployDatabase = async (name: string, component: string) => { + const timeout = "3m"; + const args = [ + "upgrade", + "--install", + name, + "oci://registry-1.docker.io/bitnamicharts/postgresql", + "-f", + `environments/${options.environment}/${component}-db.yaml`, + "-n", + launchedNetwork.kubeNamespace, + "--wait", + "--timeout", + timeout + ]; + + logger.info(`📦 Deploying ${name} database...`); + logger.debug(await $`helm ${args}`.cwd(path.join(process.cwd(), "../deploy")).text()); + logger.success(`${name} database deployed successfully`); + }; + + // Deploy Indexer database + await deployDatabase("sh-indexer-db", "sh-idxnode"); +}; + +/** + * Deploys StorageHub nodes (MSP, BSP, Indexer, Fisherman). + */ +const deployStorageHubNodes = async ( + options: DeployOptions, + launchedNetwork: LaunchedNetwork +): Promise => { + logger.info("🚀 Deploying StorageHub nodes..."); + + const deployNode = async (name: string, component: string) => { + const timeout = "5m"; + const args = [ + "upgrade", + "--install", + name, + "charts/node", + "-f", + `charts/node/storagehub/${component}.yaml`, + "-f", + `environments/${options.environment}/${component}.yaml`, + "-n", + launchedNetwork.kubeNamespace, + "--wait", + "--timeout", + timeout + ]; + + logger.info(`🏗️ Deploying ${name}...`); + logger.debug(await $`helm ${args}`.cwd(path.join(process.cwd(), "../deploy")).text()); + logger.success(`${name} deployed successfully`); + }; + + // Deploy StorageHub nodes in dependency order + await deployNode("sh-mspnode", "sh-mspnode"); + await deployNode("sh-bspnode", "sh-bspnode"); + await deployNode("sh-idxnode", "sh-idxnode"); + await deployNode("sh-fisherman", "sh-fisherman"); +}; + +/** + * Deploys StorageHub MSP Backend API. + */ +const deployStorageHubBackend = async ( + options: DeployOptions, + launchedNetwork: LaunchedNetwork +): Promise => { + logger.info("🚀 Deploying StorageHub MSP Backend API..."); + + const timeout = "3m"; + const args = [ + "upgrade", + "--install", + "sh-mspbackend", + "charts/backend", + "-f", + "charts/backend/storagehub/sh-mspbackend.yaml", + "-f", + `environments/${options.environment}/sh-mspbackend.yaml`, + "-n", + launchedNetwork.kubeNamespace, + "--wait", + "--timeout", + timeout + ]; + + logger.debug(await $`helm ${args}`.cwd(path.join(process.cwd(), "../deploy")).text()); + logger.success("StorageHub MSP Backend API deployed successfully"); +}; + +/** + * Waits for StorageHub Indexer node to be ready and registers nodes in LaunchedNetwork. + */ +const registerStorageHubNodes = async (launchedNetwork: LaunchedNetwork): Promise => { + // Forward port from indexer node to localhost for health checks + const indexerPort = 9944; + const { cleanup: indexerPortForwardCleanup } = await forwardPort( + "sh-idxnode-0", + indexerPort, + indexerPort + 100, // Use different local port to avoid conflicts + launchedNetwork + ); + + // Wait for the StorageHub Indexer to start + logger.info("⌛️ Waiting for StorageHub Indexer to start..."); + const timeoutMs = 5000; // 5 second timeout + const delayMs = 5000; // 5 second delay between iterations + await waitFor({ + lambda: async () => { + logger.info(`📡 Checking if StorageHub Indexer is ready (timeout: ${timeoutMs / 1000}s)...`); + const isReady = await isNetworkReady(indexerPort + 100, timeoutMs); + if (!isReady) { + logger.info( + `⌛️ StorageHub Indexer not ready, waiting ${delayMs / 1000}s to check again...` + ); + } + return isReady; + }, + iterations: 12, // 12 iterations of 5 + 5 = 2 minutes + delay: delayMs, + errorMessage: "StorageHub Indexer not ready" + }); + + logger.success("StorageHub Indexer is ready"); + + // Clean up the port forwarding + await indexerPortForwardCleanup(); + + // Register StorageHub nodes in LaunchedNetwork + launchedNetwork.addContainer("sh-mspnode-0", { ws: 9944 }); + launchedNetwork.addContainer("sh-bspnode-0", { ws: 9944 }); + launchedNetwork.addContainer("sh-idxnode-0", { ws: 9944 }); + launchedNetwork.addContainer("sh-fisherman-0", { ws: 9944 }); + + logger.info("📝 StorageHub nodes successfully registered in launchedNetwork."); +}; + +/** + * Checks if an image exists in Docker Hub. + * + * @param tag - The tag of the image to check. + * @returns A promise that resolves when the image is found. + */ +const checkTagExists = async (tag: string) => { + const cleanTag = tag.trim(); + logger.debug(`Checking if image ${cleanTag} is available on Docker Hub`); + const result = await $`docker manifest inspect ${cleanTag}`.nothrow().quiet(); + invariant( + result.exitCode === 0, + `❌ Image ${tag} not found.\n Does this image exist?\n Are you logged and have access to the repository?` + ); + + logger.success(`Image ${cleanTag} found on Docker Hub`); +}; diff --git a/test/cli/handlers/launch/index.ts b/test/cli/handlers/launch/index.ts index 307fcabf..4d0e79a0 100644 --- a/test/cli/handlers/launch/index.ts +++ b/test/cli/handlers/launch/index.ts @@ -10,6 +10,7 @@ import { launchDataHavenSolochain } from "./datahaven"; import { launchKurtosis } from "./kurtosis"; import { setParametersFromCollection } from "./parameters"; import { launchRelayers } from "./relayer"; +import { launchStorageHubComponents } from "./storagehub"; import { performSummaryOperations } from "./summary"; import { performValidatorOperations } from "./validator"; @@ -44,6 +45,7 @@ export interface LaunchOptions { setParameters?: boolean; relayer?: boolean; relayerImageTag: string; + storagehub?: boolean; cleanNetwork?: boolean; } @@ -94,6 +96,8 @@ const launchFunction = async (options: LaunchOptions, launchedNetwork: LaunchedN await launchRelayers(options, launchedNetwork); + await launchStorageHubComponents(options, launchedNetwork); + await performSummaryOperations(options, launchedNetwork); const fullEnd = performance.now(); const fullMinutes = ((fullEnd - timeStart) / (1000 * 60)).toFixed(1); @@ -119,7 +123,8 @@ export const launchPreActionHook = ( buildDatahaven, launchKurtosis, relayer, - setParameters + setParameters, + storagehub } = thisCmd.opts(); // Check for conflicts with --all flag @@ -132,10 +137,11 @@ export const launchPreActionHook = ( fundValidators === false || setupValidators === false || setParameters === false || - relayer === false) + relayer === false || + storagehub === false) ) { thisCmd.error( - "--all cannot be used with --no-datahaven, --no-build-datahaven, --no-launch-kurtosis, --no-deploy-contracts, --no-fund-validators, --no-setup-validators, --no-update-validator-set, --no-set-parameters, or --no-relayer" + "--all cannot be used with --no-datahaven, --no-build-datahaven, --no-launch-kurtosis, --no-deploy-contracts, --no-fund-validators, --no-setup-validators, --no-update-validator-set, --no-set-parameters, --no-relayer, or --no-storagehub" ); } @@ -149,6 +155,7 @@ export const launchPreActionHook = ( thisCmd.setOptionValue("setupValidators", true); thisCmd.setOptionValue("setParameters", true); thisCmd.setOptionValue("relayer", true); + thisCmd.setOptionValue("storagehub", true); thisCmd.setOptionValue("cleanNetwork", true); } diff --git a/test/cli/handlers/launch/storagehub.ts b/test/cli/handlers/launch/storagehub.ts new file mode 100644 index 00000000..13d8ff46 --- /dev/null +++ b/test/cli/handlers/launch/storagehub.ts @@ -0,0 +1,36 @@ +import { logger, printHeader } from "utils"; +import type { LaunchedNetwork } from "../../../launcher/types/launchedNetwork"; +import { deployStorageHubComponents } from "../deploy/storagehub"; +import type { LaunchOptions } from "."; + +/** + * Launches StorageHub components by delegating to the deploy function. + * + * @param options - Launch options. + * @param launchedNetwork - The launched network instance. + * @returns A promise that resolves when StorageHub components are launched. + */ +export const launchStorageHubComponents = async ( + options: LaunchOptions, + launchedNetwork: LaunchedNetwork +): Promise => { + // Convert launch options to deploy options format + const deployOptions = { + environment: "local" as const, // Launch is typically used for local development + skipStorageHub: !options.storagehub, + datahavenImageTag: options.datahavenImageTag, + dockerUsername: undefined, + dockerPassword: undefined, + dockerEmail: undefined + }; + + printHeader("Launching StorageHub Components"); + logger.info( + "🚀 Launching StorageHub components (MSP, BSP, Indexer, Fisherman nodes and databases)..." + ); + + // Reuse the deploy StorageHub function + await deployStorageHubComponents(deployOptions as any, launchedNetwork); + + logger.success("StorageHub components launched successfully"); +}; diff --git a/test/cli/index.ts b/test/cli/index.ts index 68f708c8..c3f44174 100644 --- a/test/cli/index.ts +++ b/test/cli/index.ts @@ -49,7 +49,7 @@ program `🫎 DataHaven: Network Deployer CLI for deploying a full DataHaven network stack to a Kubernetes cluster It will deploy: - DataHaven solochain validators (all envs), - - Storage providers (all envs) (TODO), + - StorageHub components: MSP, BSP, Indexer, Fisherman nodes and databases (local & stagenet envs), - Kurtosis Ethereum private network (stagenet env), - Snowbridge Relayers (all envs) ` @@ -98,6 +98,11 @@ program .option("--skip-validator-operations", "Skip performing validator operations", false) .option("--skip-set-parameters", "Skip setting DataHaven runtime parameters", false) .option("--skip-relayers", "Skip deploying Snowbridge Relayers", false) + .option( + "--skip-storage-hub", + "Skip deploying StorageHub components (MSP, BSP, Indexer, Fisherman, databases)", + false + ) .hook("preAction", deployPreActionHook) .action(deploy); @@ -109,7 +114,7 @@ program `🫎 DataHaven: Network Launcher CLI for launching a full DataHaven network. Complete with: - Solo-chain validators, - - Storage providers (TODO), + - StorageHub components: MSP, BSP, Indexer, Fisherman nodes and databases, - Ethereum Private network, - Snowbridge Relayers `