From 1bcbf68dc1a0eacc5258638b00bab6e21ec7946f Mon Sep 17 00:00:00 2001 From: saurabhkumarkardam Date: Mon, 22 Apr 2024 08:27:41 +0000 Subject: [PATCH] feat(substrate): platform deployment via Helm This pull request introduces enhanced functionality to deploy the Substrate platform using Helm charts. Several improvements have been made to streamline the process and increase readability: 1. Substrate-genesis: - The substrate-genesis Helm chart now dynamically generates keys and updates the genesis.json file accordingly. - Eliminates the need for the substrate-key-chart Helm chart, achieving the same functionality within the substrate-genesis chart. - Once the entire Substrate codebase is updated, substrate-key-mgmt can be safely removed. 2. Substrate-node: - The substrate-node chart handles node deployment, simplifying the overall process. - Improved readability and maintainability by removing the repetetive code and containers such as node-secrets, retrieve-chain-spec, inject-keys, and query-services. - Their functionalities have been incorporated into the main container responsible for starting the node. 3. dscp-ipfs-node - Updated for deployment via Helm. 4. A README.md has been added at the path ./platform/substrate/charts/ to provide guidance for deploying the Substrate platform via Helm. These enhancements aim to streamline Substrate deployment via Helm, making the process more efficient and the codebase easier to manage. fixes #2484 Signed-off-by: saurabhkumarkardam --- platforms/substrate/charts/README.md | 128 ++++-- .../charts/dscp-ipfs-node/Chart.yaml | 10 +- .../charts/dscp-ipfs-node/requirements.yaml | 14 + .../dscp-ipfs-node/templates/statefulset.yaml | 27 +- .../charts/dscp-ipfs-node/values.yaml | 54 ++- .../charts/substrate-genesis/Chart.yaml | 25 +- .../substrate-genesis/requirements.yaml | 11 + .../templates/generate-keys.yaml | 177 ++++++++ .../templates/genesis-job-cleanup.yaml | 56 +++ .../substrate-genesis/templates/genesis.yaml | 210 +++++++++ .../substrate-genesis/templates/job.yaml | 194 --------- .../charts/substrate-genesis/values.yaml | 97 ++--- .../charts/substrate-node/Chart.yaml | 2 +- .../charts/substrate-node/requirements.yaml | 7 + .../substrate-node/templates/statefulset.yaml | 399 +++++------------- .../charts/substrate-node/values.yaml | 191 +++++---- .../values/noproxy-and-novault/genesis.yaml | 25 ++ .../values/noproxy-and-novault/ipfs.yaml | 19 + .../values/noproxy-and-novault/node.yaml | 25 ++ 19 files changed, 962 insertions(+), 709 deletions(-) create mode 100644 platforms/substrate/charts/dscp-ipfs-node/requirements.yaml create mode 100644 platforms/substrate/charts/substrate-genesis/requirements.yaml create mode 100644 platforms/substrate/charts/substrate-genesis/templates/generate-keys.yaml create mode 100644 platforms/substrate/charts/substrate-genesis/templates/genesis-job-cleanup.yaml create mode 100644 platforms/substrate/charts/substrate-genesis/templates/genesis.yaml delete mode 100644 platforms/substrate/charts/substrate-genesis/templates/job.yaml create mode 100644 platforms/substrate/charts/substrate-node/requirements.yaml create mode 100644 platforms/substrate/charts/values/noproxy-and-novault/genesis.yaml create mode 100644 platforms/substrate/charts/values/noproxy-and-novault/ipfs.yaml create mode 100644 platforms/substrate/charts/values/noproxy-and-novault/node.yaml diff --git a/platforms/substrate/charts/README.md b/platforms/substrate/charts/README.md index 491123deeb7..574015d9347 100644 --- a/platforms/substrate/charts/README.md +++ b/platforms/substrate/charts/README.md @@ -3,43 +3,119 @@ [//]: # (SPDX-License-Identifier: Apache-2.0) [//]: # (##############################################################################################) -# Charts for Parity Substrate components +# Charts for Substrate components ## About -This folder contains helm charts which are used by the ansible playbooks for the deployment of the Parity Substrate network. Each chart folder contain a folder for templates, chart file and the corresponding value file. +This folder contains the helm charts which are used for the deployment of the Hyperledger Substrate components. Each helm that you can use has the following keys and you need to set them. The `global.cluster.provider` is used as a key for the various cloud features enabled. Also you only need to specify one cloud provider, **not** both if deploying to cloud. As of writing this doc, AWS and Azure both are fully supported. -## Example Folder Structure ### +```yaml +global: + serviceAccountName: vault-auth + cluster: + provider: aws # choose from: minikube | aws + cloudNativeServices: false # future: set to true to use Cloud Native Services + kubernetesUrl: "https://yourkubernetes.com" # Provide the k8s URL, ignore if not using Hashicorp Vault + vault: + type: hashicorp # choose from hashicorp | kubernetes + network: substrate # must be substrate for these charts + # Following are necesupplychain-subsary only when hashicorp vault is used. + addresupplychain-subs: http://vault.url:8200 + authPath: supplychain + secretEngine: secretsv2 + secretPrefix: "data/supplychain" + role: vault-role ``` -/substrate-node -|-- templates -| |-- _helpers.tpl -| |-- configmap.yaml -| |-- ingress.yaml -| |-- service.yaml -| |-- statefulset.yaml -| |-- volume.yaml -|-- Chart.yaml -|-- values.yaml +## Usage + +### Pre-requisites + +- Kubernetes Cluster (either Managed cloud option like AKS or local like minikube) +- Accessible and unsealed Hahsicorp Vault (if using Vault) +- Configured Ambassador AES (if using Ambassador as proxy) +- Update the dependencies + ``` + helm dependency update substrate-genesis + helm dependency update substrate-node + helm dependency update dscp-ipfs-node + ``` + + +## `Without Proxy and Vault` + +### 1. Install Genesis Node +```bash +# Install the genesis node +helm install genesis ./substrate-genesis --namespace supplychain-subs --create-namespace --values ./values/noproxy-and-novault/genesis.yaml ``` -## Pre-requisites +### 2. Install Bootnode +```bash +# Install bootnode +helm install validator-1 ./substrate-node --namespace supplychain-subs --values ./values/noproxy-and-novault/node.yaml --set node.isBootnode.enabled=false +``` - Helm to be installed and configured +### 3. Install Additional Nodes -## Charts description ## +To deploy additional nodes, update the following section in the `./values/noproxy-and-novault/node.yaml` file only once: +```yaml +... +node: + ... + isBootnode: + enabled: true + bootnodeName: # Here it'll be "validator-1" as defined above + bootnodeAddr: -substrate-node-0-rc-p2p. # Supporting no-proxy as of now. TODO: enable proxy method. + bootnodePort: 30333 + ... +... +``` +Then install the nodes using the following commands: +```bash +helm install validator-2 ./substrate-node --namespace supplychain-subs --values ./values/noproxy-and-novault/node.yaml -### 1. substrate-genesis ### -- This chart directory contains templates for building genesis file for the substrate network. +helm install validator-3 ./substrate-node --namespace supplychain-subs --values ./values/noproxy-and-novault/node.yaml -### 2. substrate-key-mgmt ### -- This chart directory contains templates for generating crypto material for substrate node. +helm install validator-4 ./substrate-node --namespace supplychain-subs --values ./values/noproxy-and-novault/node.yaml -### 3. substrate-node ### -- This chart directory contains templates for deploying a substrate node. +helm install member-1 ./substrate-node --namespace supplychain-subs --values ./values/noproxy-and-novault/node.yaml --set node.role=full +``` +## 4. Install IPFS Nodes -### 4. vault-k8s-mgmt ### -- This chart directory contains templates for authenticating vault with kubernetes cluster. +**4.1.** Update the following section in the `./values/noproxy-and-novault/ipfs.yaml` file only once: + +```yaml +config: + # Specify the name of any running member's node that can be considered as a bootnode for the current IPFS node. + nodeHost: -substrate-node # Here, it can be modified either as member-1-substrate-node or member-2-substrate-node +``` -### 5. dscp-ipfs-node -- This chart directory contains templates to deploy ipfs node. +**4.2.** Retrieve the `NODE_ID` from the Kubernetes secret: + +```bash +NODE_ID=$(kubectl get secret "substrate-node--keys" --namespace supplychain-subs -o jsonpath="{.data['substrate-node-keys']}" | base64 -d | jq -r '.data.node_id') +``` + +**4.3.** Now, install the IPFS nodes: + +```bash +helm install dscp-ipfs-node-1 ./dscp-ipfs-node --namespace supplychain-subs --values ./values/noproxy-and-novault/ipfs.yaml \ +--set config.ipfsBootNodeAddress="/dns4/dscp-ipfs-node-1-swarm.supplychain-subs/tcp/4001/p2p/$NODE_ID" + +helm install dscp-ipfs-node-2 ./dscp-ipfs-node --namespace supplychain-subs --values ./values/noproxy-and-novault/ipfs.yaml \ +--set config.ipfsBootNodeAddress="/dns4/dscp-ipfs-node-2-swarm.supplychain-subs/tcp/4001/p2p/$NODE_ID" +``` + +## Clean-up + +To clean up, simply uninstall the Helm releases. It's important to uninstall the genesis Helm chart at the end to prevent any cleanup failure. +```bash +helm uninstall validator-1 --namespace supplychain-subs +helm uninstall validator-2 --namespace supplychain-subs +helm uninstall validator-3 --namespace supplychain-subs +helm uninstall validator-4 --namespace supplychain-subs +helm uninstall member-1 --namespace supplychain-subs +helm uninstall dscp-ipfs-node-1 --namespace supplychain-subs +helm uninstall dscp-ipfs-node-2 --namespace supplychain-subs +helm uninstall genesis --namespace supplychain-subs +``` diff --git a/platforms/substrate/charts/dscp-ipfs-node/Chart.yaml b/platforms/substrate/charts/dscp-ipfs-node/Chart.yaml index 88cfd443a90..39fb62ec53e 100644 --- a/platforms/substrate/charts/dscp-ipfs-node/Chart.yaml +++ b/platforms/substrate/charts/dscp-ipfs-node/Chart.yaml @@ -2,7 +2,7 @@ # This Chart is a fork from https://github.com/digicatapult/helm-charts/tree/main/charts/dscp-ipfs # Please update if needed ############################################################################################## -apiVersion: v2 +apiVersion: v1 name: dscp-ipfs-node appVersion: '2.6.1' description: dscp-ipfs is a component of the DSCP project that provides a distributed IPFS based storage solution for the DSCP platform. @@ -11,14 +11,6 @@ type: application annotations: hyperledger-bevel/platform: substrate licenses: Apache-2.0 -dependencies: - - name: dscp-node - alias: dscpNode - repository: https://digicatapult.github.io/helm-charts/ - tags: - - dscp-node - version: 4.x.x - condition: dscpNode.enabled home: https://github.com/hyperledger/bevel keywords: - DSCP diff --git a/platforms/substrate/charts/dscp-ipfs-node/requirements.yaml b/platforms/substrate/charts/dscp-ipfs-node/requirements.yaml new file mode 100644 index 00000000000..38ad720bfc8 --- /dev/null +++ b/platforms/substrate/charts/dscp-ipfs-node/requirements.yaml @@ -0,0 +1,14 @@ +dependencies: + - name: dscp-node + alias: dscpNode + repository: https://digicatapult.github.io/helm-charts/ + tags: + - dscp-node + version: 4.x.x + condition: dscpNode.enabled + - name: bevel-storageclass + alias: storage + repository: "file://../../../shared/charts/bevel-storageclass" + tags: + - storage + version: ~1.0.0 diff --git a/platforms/substrate/charts/dscp-ipfs-node/templates/statefulset.yaml b/platforms/substrate/charts/dscp-ipfs-node/templates/statefulset.yaml index ef90e6ee83a..9185d246df3 100644 --- a/platforms/substrate/charts/dscp-ipfs-node/templates/statefulset.yaml +++ b/platforms/substrate/charts/dscp-ipfs-node/templates/statefulset.yaml @@ -1,3 +1,9 @@ +############################################################################################## +# Copyright Accenture. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################################################## + {{ $fullname := include "dscp-ipfs.fullname" . }} apiVersion: apps/v1 kind: StatefulSet @@ -21,7 +27,7 @@ spec: labels: name: {{ include "dscp-ipfs.fullname" . }} spec: - serviceAccountName: {{ $.Values.vault.serviceaccountname }} + serviceAccountName: {{ $.Values.global.serviceAccountName }} {{- include "dscp-ipfs.imagePullSecrets" . | indent 6 }} volumes: - name: package-manager @@ -82,21 +88,22 @@ spec: - mountPath: {{ .Values.config.ipfsDataPath }} name: ipfs-data {{- end }} - {{- if eq .Values.vault.provider "hashicorp" }} - name: ipfs-init image: {{ .Values.initContainer.image }} imagePullPolicy: {{ .Values.initContainer.pullPolicy | quote }} env: - name: MOUNT_PATH value: {{ .Values.config.ipfsDataPath }} +{{- if eq .Values.global.vault.type "hashicorp" }} - name: VAULT_ADDR - value: {{ $.Values.vault.address }} + value: {{ $.Values.global.vault.address }} - name: KUBERNETES_AUTH_PATH - value: {{ $.Values.vault.authpath }} + value: {{ $.Values.global.vault.authpath }} - name: VAULT_APP_ROLE - value: {{ $.Values.vault.role }} + value: {{ $.Values.global.vault.role }} - name: CERTS_SECRET_PREFIX - value: {{ .Values.vault.certsecretprefix }} + value: {{ .Values.global.vault.certsecretprefix }} +{{- end }} volumeMounts: - mountPath: {{ .Values.config.ipfsDataPath }} name: ipfs-data @@ -107,7 +114,7 @@ spec: args: - |- #!/usr/bin/env bash - +{{- if eq .Values.global.vault.type "hashicorp" }} echo "validating vault response" validateVaultResponse () { if echo ${2} | grep "errors"; then @@ -130,6 +137,7 @@ spec: fi } echo "done validating vault response" +{{- end }} . /scripts/package-manager.sh # Define the packages to install @@ -141,6 +149,7 @@ spec: peer_id=$(cat config | jq -r .Identity.PeerID) private_key=$(cat config | jq -r .Identity.PrivKey) +{{- if eq .Values.global.vault.type "hashicorp" }} echo " { \"data\": { @@ -168,7 +177,7 @@ spec: jq -r 'if .errors then . else .auth.client_token end') validateVaultResponse " secret $vault_secret_key" "${LOOKUP_SECRET_RESPONSE}" "LOOKUPSECRETRESPONSE" echo "Done saving keys in vault" - {{- end }} +{{- end }} containers: - name: {{ include "dscp-ipfs.fullname" . }} image: {{ .Values.image.repository }}:{{ .Values.image.tag }} @@ -252,7 +261,7 @@ spec: spec: accessModes: [ "ReadWriteOnce" ] {{- if .Values.storage.storageClass }} - storageClassName: {{ .Values.storage.storageClass }} + storageClassName: storage-{{ .Release.Name }} {{- end }} resources: requests: diff --git a/platforms/substrate/charts/dscp-ipfs-node/values.yaml b/platforms/substrate/charts/dscp-ipfs-node/values.yaml index 2ba177321bf..488aa8e0770 100644 --- a/platforms/substrate/charts/dscp-ipfs-node/values.yaml +++ b/platforms/substrate/charts/dscp-ipfs-node/values.yaml @@ -1,6 +1,36 @@ +############################################################################################## +# Copyright Accenture. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################################################## # This is a YAML-formatted file. # Declare variables to be passed into your templates. + +global: + # Provide the service account name autheticated to vault. + # NOTE: Make sure that the service account is already created and authenticated to use the vault. + # Eg. serviceAccountName: vault-auth + serviceAccountName: vault-auth + cluster: + provider: azure + cloudNativeServices: false +# Vault section contains the vault provider configuration + vault: + # Mention the vault provider. Currently hashicorp is supported + provider: kubernetes # kubernetes | hashicorp + # Provide the vault address + # Eg. address: http://vault.example.com:8200 + address: "" + # Provide the vault role used. + # Eg. role: vault-role + role: vault-role + # Provide the authpath configured to be used. + authpath: "" + # Provide the vault path where the certificates are stored + # Eg. certsecretprefix: secret/cenm-org-name + certSecretPrefix: "" + ## Provide a name to substitute for the full names of resources fullnameOverride: "" # This section contains the ipfs node config values @@ -11,7 +41,7 @@ config: # External DSCP-Node hostname to query, this overrides dscpNode.enabled nodeHost: "" # External DSCP-Node port to query - nodePort: "" + nodePort: 9944 # Public key for the IPFS subsystem publicKey: "" # Private key for the IPFS subsystem @@ -89,7 +119,7 @@ dscpNode: proxy: # Mention the proxy provider. Currently ambassador is supported # eg. provider: ambassador - provider: ambassador + provider: none # none | ambassador # url that will be added in DNS recordset # eg. external_url: test.substrate.example.com external_url: "" @@ -99,23 +129,3 @@ proxy: port: 15010 # Provide the secret name which contains the certificate certSecret: "" - -# Vault section contains the vault provider configuration -vault: - # Mention the vault provider. Currently hashicorp is supported - provider: hashicorp - # Provide the vault address - # Eg. address: http://vault.example.com:8200 - address: "" - # Provide the vault role used. - # Eg. role: vault-role - role: vault-role - # Provide the authpath configured to be used. - authpath: "" - # Provide the service account name autheticated to vault. - # NOTE: Make sure that the service account is already created and authenticated to use the vault. - # Eg. serviceaccountname: vault-auth - serviceAccountName: vault-auth - # Provide the vault path where the certificates are stored - # Eg. certsecretprefix: secret/cenm-org-name - certSecretPrefix: "" diff --git a/platforms/substrate/charts/substrate-genesis/Chart.yaml b/platforms/substrate/charts/substrate-genesis/Chart.yaml index 3c9029d1465..a15fc4d9949 100644 --- a/platforms/substrate/charts/substrate-genesis/Chart.yaml +++ b/platforms/substrate/charts/substrate-genesis/Chart.yaml @@ -3,19 +3,24 @@ # # SPDX-License-Identifier: Apache-2.0 ############################################################################################## -apiVersion: v2 +apiVersion: v1 name: substrate-genesis -appVersion: 'latest' description: A Helm chart to generate the genesis for Substrate Nodes -version: 1.0.0 type: application -annotations: - hyperledger-bevel/platform: substrate - licenses: Apache-2.0 -home: https://github.com/hyperledger/bevel +version: 1.0.0 +appVersion: latest keywords: - - DSCP - - BEVEL - - SUBSTRATE + - bevel + - ethereum + - substrate + - hyperledger + - enterprise + - blockchain + - deployment + - accenture +home: https://hyperledger-bevel.readthedocs.io/en/latest/ sources: - https://github.com/hyperledger/bevel +maintainers: + - name: Hyperledger Bevel maintainers + email: bevel@lists.hyperledger.org diff --git a/platforms/substrate/charts/substrate-genesis/requirements.yaml b/platforms/substrate/charts/substrate-genesis/requirements.yaml new file mode 100644 index 00000000000..b1195396c5f --- /dev/null +++ b/platforms/substrate/charts/substrate-genesis/requirements.yaml @@ -0,0 +1,11 @@ +dependencies: + - name: bevel-vault-mgmt + repository: "file://../../../shared/charts/bevel-vault-mgmt" + tags: + - bevel + version: ~1.0.0 + - name: bevel-scripts + repository: "file://../../../shared/charts/bevel-scripts" + tags: + - bevel + version: ~1.0.0 diff --git a/platforms/substrate/charts/substrate-genesis/templates/generate-keys.yaml b/platforms/substrate/charts/substrate-genesis/templates/generate-keys.yaml new file mode 100644 index 00000000000..14ff633cfd5 --- /dev/null +++ b/platforms/substrate/charts/substrate-genesis/templates/generate-keys.yaml @@ -0,0 +1,177 @@ + +############################################################################################## +# Copyright Accenture. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################################################## + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-generate-keys + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": post-install + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": "before-hook-creation" + labels: + app.kubernetes.io/name: pre-install-hook + app.kubernetes.io/component: keygen + app.kubernetes.io/namespace: {{ .Release.Namespace }} + app.kubernetes.io/release: {{ .Release.Name }} + app.kubernetes.io/managed-by: helm +spec: + backoffLimit: 1 + completions: 1 + template: + metadata: + labels: + app.kubernetes.io/name: pre-install-hook + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + serviceAccountName: {{ $.Values.global.serviceAccountName }} + restartPolicy: "OnFailure" + containers: + - name: generate-keys + image: {{ $.Values.node.image }} + securityContext: + runAsUser: 0 + volumeMounts: + - name: package-manager + mountPath: /scripts/package-manager.sh + subPath: package-manager.sh +{{- if eq .Values.global.vault.type "hashicorp" }} + - name: scripts-volume + mountPath: /scripts/bevel-vault.sh + subPath: bevel-vault.sh + env: + - name: VAULT_ADDR + value: {{ $.Values.global.vault.address }} + - name: KUBERNETES_AUTH_PATH + value: {{ $.Values.global.vault.authPath }} + - name: VAULT_APP_ROLE + value: {{ $.Values.global.vault.role }} + - name: CERTS_SECRET_PREFIX + value: {{ .Values.global.vault.secretPrefix }} +{{- end }} + command: ["bash", "-c"] + args: + - | + + #!/usr/bin/env bash + + echo "Step 1: Check if the node image is available and install necessary packages if needed." + {{- if ne $.Values.node.image "docker.io/paritytech/substrate-playground-template-node-template" }} + # Install necessary packages using custom package manager script + . /scripts/package-manager.sh + packages_to_install="jq bc curl unzip base58 xxd" + install_packages "$packages_to_install" + + # Check if jq is installed and download it if not installed + if ! command -v jq &> /dev/null; then + cd ~ + curl -k -L -o jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 + chmod +x jq + export PATH="$PATH:$HOME" + else + echo "jq is already installed via package-manager.sh script." + fi + {{- end }} + # Download and set up kubectl for Kubernetes management + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" + chmod +x kubectl + mv kubectl /usr/local/bin/ + kubectl version --client + + echo "Step 2: Define functions to generate keys." + generate_key() { + local scheme="$1" + local output_file="$2" + $command key generate --scheme "$scheme" --output-type json >> "$output_file" + } + + generate_nodekey() { + local node_type="$1" + local node_index="$2" + local node_id="$($command key generate-node-key --file node_key 2>&1)" + + # Generate keys for aura and grandpa + generate_key "Sr25519" "auraKeygenOutput_${node_type}${node_index}.json" + AURA_SEED=$(jq -r '.secretSeed' "auraKeygenOutput_${node_type}${node_index}.json") + AURA_ADDR=$(jq -r '.ss58Address' "auraKeygenOutput_${node_type}${node_index}.json") + + generate_key "Ed25519" "grandpaKeygenOutput_${node_type}${node_index}.json" + GRANDPA_SEED=$(jq -r '.secretSeed' "grandpaKeygenOutput_${node_type}${node_index}.json") + GRANDPA_ADDR=$(jq -r '.ss58Address' "grandpaKeygenOutput_${node_type}${node_index}.json") + + if [[ "$node_type" == "member" ]]; then + # For member nodes, also generate account keys + generate_key "Sr25519" "accKeygenOutput_${node_type}${node_index}.json" + ACCOUNT_SEED=$(jq -r '.secretSeed' "accKeygenOutput_${node_type}${node_index}.json") + ACCOUNT_ADDR=$(jq -r '.ss58Address' "accKeygenOutput_${node_type}${node_index}.json") + + echo "{ + \"data\": { + \"node_id\": \"$node_id\", + \"node_key\": \"$(cat node_key)\", + \"aura_seed\": \"$AURA_SEED\", + \"aura_addr\": \"$AURA_ADDR\", + \"grandpa_seed\": \"$GRANDPA_SEED\", + \"grandpa_addr\": \"$GRANDPA_ADDR\", + \"account_seed\": \"$ACCOUNT_SEED\", + \"account_addr\": \"$ACCOUNT_ADDR\", + \"account_file_b64\": \"$(cat accKeygenOutput_${node_type}${node_index}.json | base64 -w 0)\", + \"aura_file_b64\": \"$(cat auraKeygenOutput_${node_type}${node_index}.json | base64 -w 0)\", + \"grandpa_file_b64\": \"$(cat grandpaKeygenOutput_${node_type}${node_index}.json | base64 -w 0)\" + } + }" > finalJSON.json + secret_name="substrate-node-${node_type}-${node_index}-keys" + else + echo "{ + \"data\": { + \"node_id\": \"$node_id\", + \"node_key\": \"$(cat node_key)\", + \"aura_seed\": \"$AURA_SEED\", + \"aura_addr\": \"$AURA_ADDR\", + \"grandpa_seed\": \"$GRANDPA_SEED\", + \"grandpa_addr\": \"$GRANDPA_ADDR\", + \"aura_file_b64\": \"$(cat auraKeygenOutput_${node_type}${node_index}.json | base64 -w 0)\", + \"grandpa_file_b64\": \"$(cat grandpaKeygenOutput_${node_type}${node_index}.json | base64 -w 0)\" + } + }" > finalJSON.json + secret_name="substrate-node-${node_type}-${node_index}-keys" + fi + + # Create Kubernetes secret if it doesn't exist + if ! kubectl get secret "${secret_name}" --namespace "{{ .Release.Namespace }}" &> /dev/null; then + echo "creating secrets" + kubectl create secret generic "${secret_name}" --namespace "{{ .Release.Namespace }}" --from-file="substrate-node-keys"="finalJSON.json" + fi + } + + echo "Step 3: Initiate key generation." + main() { + command={{ $.Values.node.command }} + # Generate keys for member nodes + for ((i=1; i<= {{ $.Values.node.member.count }}; i++)); do + echo "Generating nodekey for member-$i" + generate_nodekey "member" "$i" + done + # Generate keys for validator nodes + for ((i=1; i<= {{ $.Values.node.validator.count }}; i++)); do + echo "Generating nodekey for validator-$i" + generate_nodekey "validator" "$i" + done + } + main + volumes: +{{- if eq .Values.global.vault.type "hashicorp" }} + - name: scripts-volume + configMap: + name: bevel-vault-script + defaultMode: 0777 +{{- end }} + - name: package-manager + configMap: + name: package-manager diff --git a/platforms/substrate/charts/substrate-genesis/templates/genesis-job-cleanup.yaml b/platforms/substrate/charts/substrate-genesis/templates/genesis-job-cleanup.yaml new file mode 100644 index 00000000000..e9a1af8ea5f --- /dev/null +++ b/platforms/substrate/charts/substrate-genesis/templates/genesis-job-cleanup.yaml @@ -0,0 +1,56 @@ +############################################################################################## +# Copyright Accenture. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################################################## + +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-cleanup + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-delete + "helm.sh/hook-weight": "0" + "helm.sh/hook-delete-policy": "hook-succeeded" + labels: + app: {{ .Release.Name }} + app.kubernetes.io/name: {{ .Release.Name }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + backoffLimit: 6 + template: + metadata: + labels: + app: {{ .Release.Name }} + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + restartPolicy: OnFailure + serviceAccountName: {{ $.Values.global.serviceAccountName }} + securityContext: + fsGroup: 1000 + containers: + - name: generate-genesis + image: "{{ .Values.removeGenesisOnDelete.image.repository }}:{{ .Values.removeGenesisOnDelete.image.tag }}" + securityContext: + runAsUser: 0 + imagePullPolicy: {{ .Values.removeGenesisOnDelete.image.pullPolicy }} + command: + - /bin/bash + - -c + args: + - | + {{- if .Values.removeGenesisOnDelete.enabled }} + secret_names=$(kubectl get secret -n {{ .Release.Namespace }} | grep substrate-node | awk '{print $1}') + + while IFS= read -r nodeKeys; do + kubectl delete secret "$nodeKeys" --namespace {{ .Release.Namespace }} + done <<< "$secret_names" + + if kubectl get configmap "substrate-genesis" --namespace {{ .Release.Namespace }} &> /dev/null; then + kubectl delete configmap "substrate-genesis" --namespace {{ .Release.Namespace }} + fi + {{- end}} diff --git a/platforms/substrate/charts/substrate-genesis/templates/genesis.yaml b/platforms/substrate/charts/substrate-genesis/templates/genesis.yaml new file mode 100644 index 00000000000..d2182dcc0c7 --- /dev/null +++ b/platforms/substrate/charts/substrate-genesis/templates/genesis.yaml @@ -0,0 +1,210 @@ +############################################################################################## +# Copyright Accenture. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################################################## + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": post-install + "helm.sh/hook-weight": "2" + "helm.sh/hook-delete-policy": "before-hook-creation" + labels: + app: {{ .Release.Name }} + app.kubernetes.io/name: {{ .Release.Name }} + helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/instance: {{ .Release.Name }} +spec: + backoffLimit: 6 + template: + metadata: + labels: + app: {{ .Release.Name }} + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + restartPolicy: OnFailure + serviceAccountName: {{ $.Values.global.serviceAccountName }} + securityContext: + fsGroup: 1000 + containers: + - name: generate-genesis + image: {{ $.Values.node.image }}:{{ $.Values.node.imageTag }} + imagePullPolicy: {{ $.Values.node.pullPolicy }} + volumeMounts: + - name: certcheck + mountPath: certcheck + - name: package-manager + mountPath: /scripts/package-manager.sh + subPath: package-manager.sh +{{- if eq .Values.global.vault.type "hashicorp" }} + - name: scripts-volume + mountPath: /scripts/bevel-vault.sh + subPath: bevel-vault.sh +{{- end }} + env: +{{- if eq .Values.global.vault.type "hashicorp" }} + - name: VAULT_ADDR + value: {{ $.Values.global.vault.address }} + - name: KUBERNETES_AUTH_PATH + value: {{ $.Values.global.vault.authPath }} + - name: VAULT_APP_ROLE + value: {{ $.Values.global.vault.role }} + - name: VAULT_SECRET_PREFIX + value: {{ .Values.global.vault.secretPrefix }} + - name: VAULT_SECRET_ENGINE + value: "{{ .Values.global.vault.secretEngine }}" +{{- end }} + - name: MOUNT_PATH + value: "certcheck" + command: ["bash", "-c"] + args: + - |- + + #!/usr/bin/env bash + + echo "Step 1: Check if the node image is available and install necessary packages if needed." + {{- if ne $.Values.node.image "docker.io/paritytech/substrate-playground-template-node-template" }} + # Install necessary packages using custom package manager script + . /scripts/package-manager.sh + packages_to_install="jq bc curl unzip base58 xxd" + install_packages "$packages_to_install" + + # Check if jq is installed and download it if not installed + if ! command -v jq &> /dev/null; then + cd ~ + curl -k -L -o jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 + chmod +x jq + export PATH="$PATH:$HOME" + else + echo "jq is already installed via package-manager.sh script." + fi + {{- end }} + # Download and set up kubectl for Kubernetes management + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" + chmod +x kubectl + mv kubectl /usr/local/bin/ + kubectl version --client + + echo "Step 2: Execute the provided command to generate the genesis block." + mkdir certcheck + command={{ $.Values.node.command }} + echo "Generate genesis" + GENESIS=$($command build-spec --disable-default-bootnode --chain local) + + echo "Step 3: Edit genesis configuration." + # Set values + GENESIS=$(echo $GENESIS | jq '.name |= {{ .Values.chain | quote }}') + GENESIS=$(echo $GENESIS | jq '.id |= {{ .Values.chain | replace "-" "_" | quote }}') + GENESIS=$(echo $GENESIS | jq '.chainType |= "Live"') + # Clear authorities and balances + GENESIS=$(echo $GENESIS | jq '.genesis.runtime.aura.authorities |= []') + GENESIS=$(echo $GENESIS | jq '.genesis.runtime.grandpa.authorities |= []') + GENESIS=$(echo $GENESIS | jq '.genesis.runtime.balances.balances |= []') + {{- if eq $.Values.node.image "ghcr.io/inteli-poc/dscp-node" }} + GENESIS=$(echo $GENESIS | jq '.genesis.runtime.nodeAuthorization.nodes |= []') + GENESIS=$(echo $GENESIS | jq '.genesis.runtime.membership.members |= []') + {{- end }} + + echo "Step 4: Generate sudo key with Sr25519 scheme and add sudo account key and balance into genesis." + # Generate sudo key with Sr25519 scheme + $command key generate --scheme Sr25519 --output-type json >> certcheck/sudoKeygenOutput.json + SUDO_SEED=$(jq -r '.secretPhrase' certcheck/sudoKeygenOutput.json) + SUDO_ADDR=$(jq -r '.ss58Address' certcheck/sudoKeygenOutput.json) + # Add sudo account key and balance into genesis + GENESIS=$(echo $GENESIS | jq --arg sudo $SUDO_ADDR --arg balance 1152921504606846976 '.genesis.runtime.balances.balances += [[$sudo, ($balance | tonumber)]]') + GENESIS=$(echo $GENESIS | jq --arg sudo $SUDO_ADDR '.genesis.runtime.sudo.key |= $sudo') + + echo "Step 5: Insert AURA & GRANDPA keys into genesis for validators." + echo "Inserting keys into genesis for validators" + for ((i=1; i<= {{ $.Values.node.validator.count }}; i++)); do + secret_data=$(kubectl get secret "substrate-node-validator-${i}-keys" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d) + + # Check if secret_data is empty or not + if [ -n "$secret_data" ]; then + # Extract aura_addr and grandpa_addr + aura_addr=$(echo "$secret_data" | jq -r '.data.aura_addr') + grandpa_addr=$(echo "$secret_data" | jq -r '.data.grandpa_addr') + + # Check if extraction successful + if [ -n "$aura_addr" ] && [ -n "$grandpa_addr" ]; then + # Insert aura_addr keys into GENESIS JSON + GENESIS=$(echo "$GENESIS" | jq --arg aura "$aura_addr" '.genesis.runtime.aura.authorities += [$aura]') + GENESIS=$(echo "$GENESIS" | jq --arg grandpa "$grandpa_addr" '.genesis.runtime.grandpa.authorities += [[$grandpa, 1]]') + else + echo "Error: Unable to retrieve aura_addr or grandpa_addr key" + fi + else + echo "Error: Unable to retrieve data." + fi + done + + # Initialize an array to store each nodes' Kubernetes secret name + secret_names=$(kubectl get secret -n {{ .Release.Namespace }} | grep substrate-node | awk '{print $1}') + declare -a nodes + while IFS= read -r line; do + nodes+=("$line") + done <<< "$secret_names" + + echo "Step 6: Adding member accounts and their balances to genesis." + for ((i=1; i<= {{ $.Values.node.member.count }}; i++)); do + account_addr=$(kubectl get secret "substrate-node-member-$i-keys" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.account_addr') + # Add account address and balance into genesis block + GENESIS=$(echo "$GENESIS" | jq --arg account_id "$account_addr" --arg balance {{ $.Values.node.member.balance }} '.genesis.runtime.balances.balances += [[$account_id, ($balance | tonumber)]]') + GENESIS=$(echo "$GENESIS" | jq --arg account_id "$account_addr" '.genesis.runtime.membership.members += [$account_id]') + + # Loop through each node to add authorization for the current member + for node in "${nodes[@]}"; do + # Retrieve node ID for the current node + node_id=$(kubectl get secret "$node" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.node_id') + + # Convert node ID to base58 format + base58=$(echo -n "$node_id" | base58 -d | xxd -p | tr -d '[:space:]' | tr '[:lower:]' '[:upper:]') + + # Split the base58 string into an array of bytes + arr_node_id=($(echo $base58 | fold -w2)) + + # Add authorization for the current member to the node + GENESIS=$(echo $GENESIS | jq --arg owner "$account_addr" '.genesis.runtime.nodeAuthorization.nodes += [[[], $owner]]') + for byte in "${arr_node_id[@]}" + do + # Add each byte of the node ID to the authorization + GENESIS=$(echo $GENESIS | jq --arg byte $(echo "obase=10; ibase=16; $byte" | bc) '.genesis.runtime.nodeAuthorization.nodes[-1][0] += [($byte | tonumber)]') + done + done + done + + echo "Step 7: Update the format of the modified genesis JSON and create a config map if it doesn't exist." + # Write the modified genesis JSON to a file + echo "$GENESIS" > certcheck/genesis.json + # Convert the genesis JSON to raw format + echo "Converting genesis to raw format" + GENESIS=$($command build-spec --disable-default-bootnode --raw --chain certcheck/genesis.json) + echo "$GENESIS" > certcheck/genesis_raw.json + # Encode the raw genesis JSON to base64 + cat certcheck/genesis_raw.json | base64 -w0 > certcheck/genesis_base64 # No need to encode it if you wanna store genesis as a K8s secret + + # # Create the config map "substrate-genesis" using the base64 encoded genesis JSON and sudoKeygenOutput.json if it doesn't exist + if ! kubectl get configmap "substrate-genesis" --namespace {{ .Release.Namespace }} &> /dev/null; then + kubectl create configmap "substrate-genesis" --namespace {{ .Release.Namespace }} --from-file=genesis="${MOUNT_PATH}/genesis_base64" --from-file=sudoKeygenOutput="${MOUNT_PATH}/sudoKeygenOutput.json" + fi + echo "COMPLETED!" + volumes: + - name: certcheck + emptyDir: + medium: Memory + - name: package-manager + configMap: + name: package-manager +{{- if eq .Values.global.vault.type "hashicorp" }} + - name: scripts-volume + configMap: + name: bevel-vault-script + defaultMode: 0777 +{{- end }} diff --git a/platforms/substrate/charts/substrate-genesis/templates/job.yaml b/platforms/substrate/charts/substrate-genesis/templates/job.yaml deleted file mode 100644 index f08c1ee8e43..00000000000 --- a/platforms/substrate/charts/substrate-genesis/templates/job.yaml +++ /dev/null @@ -1,194 +0,0 @@ -############################################################################################## -# Copyright Accenture. All Rights Reserved. -# -# SPDX-License-Identifier: Apache-2.0 -############################################################################################## - -apiVersion: batch/v1 -kind: Job -metadata: - name: "{{ $.Values.metadata.name }}" - namespace: "{{ $.Values.metadata.namespace }}" - labels: - app: "{{ $.Values.metadata.name }}" - app.kubernetes.io/name: "{{ $.Values.metadata.name }}" - helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/instance: {{ .Release.Name }} - {{- include "labels.custom" . | nindent 2 }} -spec: - backoffLimit: 6 - template: - metadata: - labels: - app: "{{ $.Values.metadata.name }}" - app.kubernetes.io/name: "{{ $.Values.metadata.name }}" - app.kubernetes.io/instance: {{ .Release.Name }} - {{- include "labels.custom" . | nindent 2 }} - spec: - restartPolicy: OnFailure - serviceAccountName: {{ $.Values.vault.serviceaccountname }} - securityContext: - fsGroup: 1000 - containers: - - name: generate-genesis - image: {{ $.Values.node.image }}:{{ $.Values.node.imageTag }} - imagePullPolicy: {{ $.Values.node.pullPolicy }} - volumeMounts: - - name: certcheck - mountPath: certcheck - - name: package-manager - mountPath: /scripts/package-manager.sh - subPath: package-manager.sh - env: - - name: VAULT_ADDR - value: {{ $.Values.vault.address }} - - name: KUBERNETES_AUTH_PATH - value: {{ $.Values.vault.authpath }} - - name: VAULT_APP_ROLE - value: {{ $.Values.vault.role }} - - name: MOUNT_PATH - value: "certcheck" - - name: CERTS_SECRET_PREFIX - value: {{ .Values.vault.certsecretprefix }} - command: ["bash", "-c"] - args: - - |- - #!/usr/bin/env bash - - {{- if ne $.Values.node.image "docker.io/paritytech/substrate-playground-template-node-template" }} - . /scripts/package-manager.sh - # Define the packages to install - packages_to_install="jq bc curl unzip" - install_packages "$packages_to_install" - - if [[ $? > 0 ]] - then - # download jq - cd ~; - curl -k -L -o jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64; - chmod +x jq; - export PATH=$PATH:.; - else - echo "jq and curl was installed using apt-get." - fi; - {{- end }} - - validateVaultResponse () { - if echo ${2} | grep "errors"; then - echo "ERROR: unable to retrieve ${1}: ${2}" - exit 1 - fi - if [ "$3" == "LOOKUPSECRETRESPONSE" ] - then - http_code=$(curl -sS -o /dev/null -w "%{http_code}" \ - --header "X-Vault-Token: ${VAULT_CLIENT_TOKEN}" \ - ${VAULT_ADDR}/v1/${vault_secret_key}) - curl_response=$? - if test "$http_code" != "200" ; then - echo "Http response code from Vault - $http_code" - if test "$curl_response" != "0"; then - echo "Error: curl command failed with error code - $curl_response" - exit 1 - fi - fi - fi - } - - mkdir certcheck - command={{ $.Values.node.command }} - echo "Generate genesis" - GENESIS=$($command build-spec --disable-default-bootnode --chain local) - GENESIS=$(echo $GENESIS | jq '.name |= {{ .Values.chain | quote }}') - GENESIS=$(echo $GENESIS | jq '.id |= {{ .Values.chain | replace "-" "_" | quote }}') - GENESIS=$(echo $GENESIS | jq '.chainType |= "Live"') - - echo "Editing genesis config" - GENESIS=$(echo $GENESIS | jq '.genesis.runtime.aura.authorities |= []') - GENESIS=$(echo $GENESIS | jq '.genesis.runtime.grandpa.authorities |= []') - GENESIS=$(echo $GENESIS | jq '.genesis.runtime.balances.balances |= []') - {{- if eq $.Values.node.image "ghcr.io/inteli-poc/dscp-node" }} - GENESIS=$(echo $GENESIS | jq '.genesis.runtime.nodeAuthorization.nodes |= []') - GENESIS=$(echo $GENESIS | jq '.genesis.runtime.membership.members |= []') - {{- end }} - - echo "Generating sudo key with scheme Sr25519..." - $command key generate --scheme Sr25519 --output-type json >> certcheck/sudoKeygenOutput.json - SUDO_SEED=$(jq -r '.secretPhrase' certcheck/sudoKeygenOutput.json) - SUDO_ADDR=$(jq -r '.ss58Address' certcheck/sudoKeygenOutput.json) - - echo "Adding sudo account key and balance into genesis" - GENESIS=$(echo $GENESIS | jq --arg sudo $SUDO_ADDR --arg balance 1152921504606846976 '.genesis.runtime.balances.balances += [[$sudo, ($balance | tonumber)]]') - GENESIS=$(echo $GENESIS | jq --arg sudo $SUDO_ADDR '.genesis.runtime.sudo.key |= $sudo') - - echo "Inserting keys into genesis" - - echo "Inserting aura keys into genesis" - {{- range .Values.aura_keys }} - echo {{.}} - GENESIS=$(echo "$GENESIS" | jq --arg aura {{.}} '.genesis.runtime.aura.authorities += [$aura]') - {{- end }} - - echo "Inserting grandpa keys into genesis" - {{- range .Values.grandpa_keys }} - echo {{.}} - GENESIS=$(echo "$GENESIS" | jq --arg grandpa {{.}} '.genesis.runtime.grandpa.authorities += [[$grandpa, 1]]') - {{- end }} - - {{- if eq $.Values.node.image "ghcr.io/inteli-poc/dscp-node" }} - echo "Adding member accounts and their balances to genesis" - {{- range $idx, $member := .Values.members }} - GENESIS=$(echo $GENESIS | jq --arg account_id {{ $member.account_id }} --arg balance {{ $member.balance }} '.genesis.runtime.balances.balances += [[$account_id, ($balance | tonumber)]]') - GENESIS=$(echo $GENESIS | jq --arg account_id {{ $member.account_id }} '.genesis.runtime.membership.members += [$account_id]') - - {{- range $member.nodes }} - this_node_id={{ . }} - arr_node_id=($(echo $this_node_id | fold -w2)) - GENESIS=$(echo $GENESIS | jq --arg owner {{ $member.account_id }} '.genesis.runtime.nodeAuthorization.nodes += [[[], $owner]]') - for byte in "${arr_node_id[@]}" - do - GENESIS=$(echo $GENESIS | jq --arg byte $(echo "obase=10; ibase=16; $byte" | bc) '.genesis.runtime.nodeAuthorization.nodes[-1][0] += [($byte | tonumber)]') - done - {{- end }} - {{- end }} - {{- end }} - - echo "$GENESIS" > certcheck/genesis.json - echo "********* check genesis *********" - cat certcheck/genesis.json - echo "Converting genesis to raw format" - GENESIS=$($command build-spec --disable-default-bootnode --raw --chain certcheck/genesis.json) - echo "$GENESIS" > certcheck/genesis_raw.json - cat certcheck/genesis_raw.json | base64 -w0 > certcheck/genesis_base64 - - KUBE_SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) - echo "Getting secrets from Vault Server: ${VAULT_ADDR}" - - # Login to Vault and so I can get an approle token - export VAULT_TOKEN=$(curl -sS --request POST ${VAULT_ADDR}/v1/auth/${KUBERNETES_AUTH_PATH}/login \ - -H "Content-Type: application/json" \ - -d '{"role":"'"${VAULT_APP_ROLE}"'","jwt":"'"${KUBE_SA_TOKEN}"'"}' | \ - jq -r 'if .errors then . else .auth.client_token end') - - # the vault cli is required for this job as the genesis file is too large to be passed in via a vault api call - echo "installing vault cli" - curl -O -L https://releases.hashicorp.com/vault/1.7.1/vault_1.7.1_linux_amd64.zip - unzip vault_1.7.1_linux_amd64.zip - {{- if eq $.Values.node.image "docker.io/paritytech/substrate-playground-template-node-template" }} - export PATH=$PATH:~/workspace - {{- else }} - mv vault /bin - {{- end }} - vault --version - - validateVaultResponse 'vault login token' "${VAULT_TOKEN}" - vault_secret_key="${CERTS_SECRET_PREFIX}/genesis" - # Save the generated keys to VAULT - vault kv put $vault_secret_key genesis="@${MOUNT_PATH}/genesis_base64" sudo_details="@${MOUNT_PATH}/sudoKeygenOutput.json" - volumes: - - name: certcheck - emptyDir: - medium: Memory - - name: package-manager - configMap: - name: package-manager diff --git a/platforms/substrate/charts/substrate-genesis/values.yaml b/platforms/substrate/charts/substrate-genesis/values.yaml index 821c80ff910..4f4e5efaa1f 100644 --- a/platforms/substrate/charts/substrate-genesis/values.yaml +++ b/platforms/substrate/charts/substrate-genesis/values.yaml @@ -11,14 +11,42 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. -metadata: - #Provide the namespace for organization's peer - #Eg. namespace: carrier-subs - namespace: default +# The following are for overriding global values +global: + # Provide the service account name which will be created. + serviceAccountName: vault-auth + cluster: + provider: azure # choose from: minikube | aws + cloudNativeServices: false # 'false' is implemented + #Provide the kubernetes host url + #Eg. kubernetesUrl: https://10.3.8.5:6443 + kubernetesUrl: + vault: + # Provide the type of vault + type: kubernetes # hashicorp | kubernetes + # Provide the vault role used. + role: vault-role + # Provide the network type + network: substrate + # Provide the vault server address + address: # "http://vault_url" + # Provide the vault authPath configured to be used. + authPath: supplychain + # Provide the secret engine. + secretEngine: secretsv2 + # Provide the vault path where the secrets will be stored + secretPrefix: "data/supplychain" + +removeGenesisOnDelete: + enabled: true + image: + repository: ghcr.io/hyperledger/bevel-k8s-hooks + tag: qgt-0.2.12 + pullPolicy: IfNotPresent - #Provide the name for substrate-key-mgmt job release - #Eg. name: carrier-keys-job - name: substrate-genesis-job +# Provide custom chain name +# Eg. chain: inteli-gcp +chain: inteli node: # Pull substrate Docker image @@ -31,53 +59,8 @@ node: # Command to be invoked to perform operations on the node # Eg. command: substrate command: ./dscp-node - -############################################################# -# HashiCorp Vault Configuration # -############################################################# -# NOTE: Make sure that the vault is already unsealed, intialized and configured to -# use Kubernetes service account token based authentication. -# For more info, see https://www.vaultproject.io/docs/auth/kubernetes - -vault: - # Provide the vault address - # Eg. address: http://vault.example.com:8200 - address: "" - # Provide the vault role used. - # Eg. role: vault-role - role: vault-role - # Provide the authpath configured to be used. - authpath: "" - # Provide the service account name autheticated to vault. - # NOTE: Make sure that the service account is already created and autheticated to use the vault. - # Eg. serviceaccountname: vault-auth - serviceAccountName: vault-auth - # Provide the vault path where the certificates are stored - # Eg. certsecretprefix: secret/cenm-org-name - certSecretPrefix: "" - -# Provide custom chain name -# Eg. chain: inteli-gcp -chain: inteli -# Provide the aura keys in a list format -# e.g. -# aura_keys: -# - 5DyCUqDTSgTXcL1B7i7KMBcVBvGdtxXLXZ6uEi5Ktekj5tQF -# - 5GBbtj2twDjJfncE6RtLibzjezH8xghRoRD1dDbZFxsKQjuk -aura_keys: [] -# Provide the grandpa keys in a list format -# e.g. -# grandpa_keys: -# - 5EtJgUviLmr1RCNhb7jttY6bX5VUHneL6Uyno6rLyGtawGzA -# - 5FwRY6PZ1fkyJUcKgVN5Pv6hzzPZZ31A49UuSXjmciL36LH1 -grandpa_keys: [] - -# Provide array of member details -# e.g. -# members: -# - account_id: 5GHW6ZUNk8Hoh4ZEtRnYcx7hvbQdrqqvi7NUBo5YaGSkdxrS -# balance: 1152921504606846976 -# nodes: -# - 0024080112200B290101F0A19F007C6C70EE4CA8430FC349DF6E2C8EED770B69F09AFBD48A19 -# - 00240801122023F888BC544900B3876ED8F9C7BE6A92C4BB1A2A5030396DD70EE0E02EA534FA -members: [] + validator: + count: 4 + member: + count: 1 + balance: 1152921504606846976 diff --git a/platforms/substrate/charts/substrate-node/Chart.yaml b/platforms/substrate/charts/substrate-node/Chart.yaml index 16c122ed066..75675ef500e 100644 --- a/platforms/substrate/charts/substrate-node/Chart.yaml +++ b/platforms/substrate/charts/substrate-node/Chart.yaml @@ -2,7 +2,7 @@ # This Chart is a fork from https://github.com/paritytech/helm-charts # Please update if needed ############################################################################################## -apiVersion: v2 +apiVersion: v1 name: substrate-node description: A Helm chart to deploy Substrate/Polkadot nodes type: application diff --git a/platforms/substrate/charts/substrate-node/requirements.yaml b/platforms/substrate/charts/substrate-node/requirements.yaml new file mode 100644 index 00000000000..895f0a0e1cf --- /dev/null +++ b/platforms/substrate/charts/substrate-node/requirements.yaml @@ -0,0 +1,7 @@ +dependencies: + - name: bevel-storageclass + alias: storage + repository: "file://../../../shared/charts/bevel-storageclass" + tags: + - storage + version: ~1.0.0 diff --git a/platforms/substrate/charts/substrate-node/templates/statefulset.yaml b/platforms/substrate/charts/substrate-node/templates/statefulset.yaml index 25ea5b4783c..8a6dfff968a 100644 --- a/platforms/substrate/charts/substrate-node/templates/statefulset.yaml +++ b/platforms/substrate/charts/substrate-node/templates/statefulset.yaml @@ -167,149 +167,14 @@ spec: readOnly: true {{- end }} {{- end }} - - name: node-secrets - image: {{ .Values.vault.image }} - imagePullPolicy: IfNotPresent - env: - - name: VAULT_ADDR - value: {{ .Values.vault.address }} - - name: VAULT_SECRET_PREFIX - value: {{ .Values.vault.secretPrefix }} - - name: KUBERNETES_AUTH_PATH - value: {{ .Values.vault.authPath }} - - name: VAULT_APP_ROLE - value: {{ .Values.vault.appRole }} - - name: PEER_NAME - value: {{ .Values.node.name }} - command: ["/bin/sh", "-c"] - args: - - |- - #!/bin/sh - validateVaultResponse () { - if echo ${2} | grep "errors"; then - echo "ERROR: unable to retrieve ${1}: ${2}" - exit 1 - fi - if [ "$3" == "LOOKUPSECRETRESPONSE" ] - then - http_code=$(curl -sS -o /dev/null -w "%{http_code}" \ - --header "X-Vault-Token: ${VAULT_CLIENT_TOKEN}" \ - ${VAULT_ADDR}/v1/${vault_secret_key}) - curl_response=$? - if test "$http_code" != "200" ; then - echo "Http response code from Vault - $http_code" - if test "$curl_response" != "0"; then - echo "Error: curl command failed with error code - $curl_response" - exit 1 - fi - fi - fi - } - - KUBE_SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) - echo "Getting secrets from Vault Server: ${VAULT_ADDR}" - - ## Login to Vault to get an app role token ## - VAULT_CLIENT_TOKEN=$(curl -sS --request POST ${VAULT_ADDR}/v1/auth/${KUBERNETES_AUTH_PATH}/login \ - -H "Content-Type: application/json" \ - -d '{"role":"'"${VAULT_APP_ROLE}"'","jwt":"'"${KUBE_SA_TOKEN}"'"}' | \ - jq -r 'if .errors then . else .auth.client_token end') - validateVaultResponse 'vault login token' "${VAULT_CLIENT_TOKEN}" - echo "logged in" - - vault_secret_key="${VAULT_SECRET_PREFIX}/${PEER_NAME}/substrate" - - echo "Getting node-key, aura and grandpa secret seeds from $vault_secret_key" - - LOOKUP_SECRET_RESPONSE=$(curl -sS \ - --header "X-Vault-Token:${VAULT_CLIENT_TOKEN}" \ - ${VAULT_ADDR}/v1/${vault_secret_key} | \ - jq -r 'if .errors then . else . end') - validateVaultResponse "secret (${vault_secret_key})" "${LOOKUP_SECRET_RESPONSE}" "LOOKUPSECRETRESPONSE" - - {{- range $keys := .Values.node.keys }} - secretSeed=$(echo ${LOOKUP_SECRET_RESPONSE} | jq -r '.data.data["{{ .seed }}"]') - echo "${secretSeed}" > /secrets/{{ .seed }} - {{- end }} - - node_key=$(echo ${LOOKUP_SECRET_RESPONSE} | jq -r '.data.data["node_key"]') - echo "${node_key}" > /secrets/node_key - volumeMounts: - - name: keystore - mountPath: /secrets - readOnly: false - - name: retrieve-chain-spec - image: {{ .Values.vault.image }} - imagePullPolicy: IfNotPresent - env: - - name: VAULT_ADDR - value: {{ .Values.vault.address }} - - name: VAULT_SECRET_PREFIX - value: {{ .Values.vault.secretPrefix }} - - name: KUBERNETES_AUTH_PATH - value: {{ .Values.vault.authPath }} - - name: VAULT_APP_ROLE - value: {{ .Values.vault.appRole }} - command: ["/bin/sh", "-c"] - args: - - |- - #!/bin/sh - - validateVaultResponse () { - if echo ${2} | grep "errors"; then - echo "ERROR: unable to retrieve ${1}: ${2}" - exit 1 - fi - if [ "$3" == "LOOKUPSECRETRESPONSE" ] - then - http_code=$(curl -sS -o /dev/null -w "%{http_code}" \ - --header "X-Vault-Token: ${VAULT_CLIENT_TOKEN}" \ - ${VAULT_ADDR}/v1/${vault_secret_key}) - curl_response=$? - if test "$http_code" != "200" ; then - echo "Http response code from Vault - $http_code" - if test "$curl_response" != "0"; then - echo "Error: curl command failed with error code - $curl_response" - exit 1 - fi - fi - fi - } - - KUBE_SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) - echo "Getting secrets from Vault Server: ${VAULT_ADDR}" - - ## Login to Vault to get an app role token ## - VAULT_CLIENT_TOKEN=$(curl -sS --request POST ${VAULT_ADDR}/v1/auth/${KUBERNETES_AUTH_PATH}/login \ - -H "Content-Type: application/json" \ - -d '{"role":"'"${VAULT_APP_ROLE}"'","jwt":"'"${KUBE_SA_TOKEN}"'"}' | \ - jq -r 'if .errors then . else .auth.client_token end') - validateVaultResponse 'vault login token' "${VAULT_CLIENT_TOKEN}" - echo "logged in" - - vault_secret_key="${VAULT_SECRET_PREFIX}/genesis" - - echo "Getting the chain spec from $vault_secret_key" - - LOOKUP_SECRET_RESPONSE=$(curl -sS \ - --header "X-Vault-Token:${VAULT_CLIENT_TOKEN}" \ - ${VAULT_ADDR}/v1/${vault_secret_key} | \ - jq -r 'if .errors then . else . end') - validateVaultResponse "secret (${vault_secret_key})" "${LOOKUP_SECRET_RESPONSE}" "LOOKUPSECRETRESPONSE" - - chain_spec=$(echo ${LOOKUP_SECRET_RESPONSE} | jq -r '.data.data["genesis"]') - echo "${chain_spec}" | base64 -d > {{ .Values.node.customChainspecPath }} - volumeMounts: - - name: chain-data - mountPath: /data - {{- if or .Values.node.customChainspecUrl .Values.node.collator.relayChainCustomChainspecUrl }} + {{- if and .Values.node.customChainspecUrl .Values.node.collator.relayChainCustomChainspecUrl }} - name: download-chainspec image: {{ .Values.initContainer.image.repository }}:{{ .Values.initContainer.image.tag }} command: [ "/bin/sh" ] args: - -c - | - {{- if .Values.node.customChainspecUrl }} + {{- if and (.Values.node.customChainspecUrl) (.Values.node.customChainspecPath) }} if [ ! -f {{ .Values.node.customChainspecPath }} ]; then wget -O {{ .Values.node.customChainspecPath }} {{ .Values.node.customChainspecUrl }} fi @@ -323,171 +188,123 @@ spec: - name: chain-data mountPath: /data {{- end }} - {{- if .Values.node.keys }} - - name: inject-keys + containers: + - name: {{ .Values.node.chain }} image: {{ .Values.image.repository }}:{{ .Values.image.tag }} imagePullPolicy: {{ .Values.image.pullPolicy }} command: ["/bin/sh", "-c"] args: - |- - {{- range $keys := .Values.node.keys }} - {{ $.Values.node.command }} key insert --base-path /data \ - {{- if $.Values.vault.secretPrefix }} - --chain {{ $.Values.node.customChainspecPath }} \ - --key-type {{ .type }} \ - --scheme {{ .scheme }} \ - --suri /secrets/{{ .seed }} \ - {{- else }} - --chain ${CHAIN} \ - --key-type $(cat /var/run/secrets/{{ .type }}/type) \ - --scheme $(cat /var/run/secrets/{{ .type }}/scheme) \ - --suri /var/run/secrets/{{ .type }}/seed \ + + #!/bin/sh + + # STEP-1 + echo "Step 1: Install necessary packages using custom package manager script" + . /scripts/package-manager.sh + packages_to_install="jq curl" + install_packages "$packages_to_install" + + # STEP-2 + echo "STEP-2: Download and set up kubectl for Kubernetes management" + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" + chmod +x kubectl + mv kubectl /usr/local/bin/ + kubectl version --client + + # STEP-3 + echo "Step 3: Extract chain specification (genesis) from Kubernetes ConfigMap and store it" + chain_spec=$(kubectl get configmap "substrate-genesis" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["genesis"]') + echo "${chain_spec}" | base64 -d > {{ .Values.node.customChainspecPath }} + + # STEP-4 + echo "Step 4: Retrieve secret keys from Kubernetes Secrets if available" + secretName="substrate-node-{{ .Release.Name }}-keys" + if kubectl get secret "${secretName}" --namespace {{ .Release.Namespace }} >/dev/null 2>&1; then + # Extract AURA secret phrase from Kubernetes Secret + AURA_SECRETPHRASE=$(kubectl get secret "${secretName}" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.aura_file_b64' | base64 -d | jq -r '.secretPhrase') + # Extract GRANDPA secret phrase from Kubernetes Secret + GRAN_SECRETPHRASE=$(kubectl get secret "${secretName}" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.grandpa_file_b64' | base64 -d | jq -r '.secretPhrase') + # Extract NODE_KEY from Kubernetes Secret + NODE_KEY=$(kubectl get secret "${secretName}" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.node_key') + fi + # Check if bootnode is enabled + {{- if .Values.node.isBootnode.enabled }} + # Retrieve BOOTNODE_ID from Kubernetes Secret + BOOTNODE_ID=$(kubectl get secret "substrate-node-{{ .Values.node.isBootnode.bootnodeName }}-keys" --namespace {{ .Release.Namespace }} -o json | jq -r '.data["substrate-node-keys"]' | base64 -d | jq -r '.data.node_id') {{- end }} - && echo "Inserted key {{ .type }} into Keystore" \ - || echo "Failed to insert key {{ .type}} into Keystore." - {{- end }} - env: - - name: CHAIN - value: {{ .Values.node.chain }} - volumeMounts: - - mountPath: /secrets - name: keystore - - mountPath: /data - name: chain-data - {{- range $keys := .Values.node.keys }} - - mountPath: /var/run/secrets/{{ .type }} - name: {{ .type }} - {{- end }} - {{- end }} - {{- if .Values.node.perNodeServices.createP2pService }} - - name: query-services - image: {{ .Values.kubectl.image.repository }}:{{ .Values.kubectl.image.tag }} - command: [ "/bin/sh" ] - args: - - -c - - | + + # STEP-5 + echo "Step 5: Insert keys into Keystore using dscp-node command-line tool" + # Insert AURA key into Keystore + ./{{ .Values.node.command }} key insert --base-path=/data --chain=/data/chainspec.json --key-type=aura --scheme=Sr25519 --suri="${AURA_SECRETPHRASE}" && echo "Inserted key aura into Keystore" || echo "Failed to insert key aura into Keystore." + # Insert GRANPA key into Keystore + ./{{ .Values.node.command }} key insert --base-path=/data --chain=/data/chainspec.json --key-type=gran --scheme=Ed25519 --suri="${GRAN_SECRETPHRASE}" && echo "Inserted key gran into Keystore" || echo "Failed to insert key gran into Keystore." + + # STEP-6 + echo "Step 6: Determine various ports and external addresses for P2P communication" POD_INDEX="${HOSTNAME##*-}" - {{- if eq .Values.node.perNodeServices.p2pServiceType "NodePort" }} - RELAY_CHAIN_P2P_PORT="$(kubectl --namespace {{ .Release.Namespace }} get service {{ $fullname }}-${POD_INDEX}-rc-p2p -o jsonpath='{.spec.ports[*].nodePort}')" - echo -n "${RELAY_CHAIN_P2P_PORT}" > /data/relay_chain_p2p_port - echo "Retrieved Kubernetes service node port from {{ $fullname }}-${POD_INDEX}-rc-p2p, saved ${RELAY_CHAIN_P2P_PORT} to /data/relay_chain_p2p_port" + {{- if and (.Values.node.perNodeServices.createP2pService) (eq .Values.node.perNodeServices.p2pServiceType "NodePort") }} + RELAY_CHAIN_P2P_PORT=$(kubectl --namespace {{ .Release.Namespace }} get service {{ $fullname }}-${POD_INDEX}-rc-p2p -o jsonpath='{.spec.ports[*].nodePort}') {{- else if or (eq .Values.node.perNodeServices.p2pServiceType "LoadBalancer") (eq .Values.node.perNodeServices.p2pServiceType "ClusterIP") }} - RELAY_CHAIN_P2P_PORT=30333 - echo -n "${RELAY_CHAIN_P2P_PORT}" > /data/relay_chain_p2p_port - echo "Kubernetes service {{ $fullname }}-${POD_INDEX}-rc-p2p is ${RELAY_CHAIN_P2P_PORT}" + RELAY_CHAIN_P2P_PORT=8080 # Default port {{- end }} - {{- if and .Values.node.collator.isParachain (eq .Values.node.perNodeServices.p2pServiceType "Nodeport") }} - PARA_CHAIN_P2P_PORT="$(kubectl --namespace {{ .Release.Namespace }} get service {{ $fullname }}-${POD_INDEX}-pc-p2p -o jsonpath='{.spec.ports[*].nodePort}')" - echo -n "${PARA_CHAIN_P2P_PORT}" > /data/para_chain_p2p_port - echo "Retrieved Kubernetes service node port from {{ $fullname }}-${POD_INDEX}-pc-p2p, saved ${PARA_CHAIN_P2P_PORT} to /data/para_chain_p2p_port" - {{- else if and .Values.node.collator.isParachain (or (eq .Values.node.perNodeServices.p2pServiceType "LoadBalancer") (eq .Values.node.perNodeServices.p2pServiceType "ClusterIP")) }} - PARA_CHAIN_P2P_PORT=30334 - echo -n "${PARA_CHAIN_P2P_PORT}" > /data/para_chain_p2p_port - echo "Kubernetes service {{ $fullname }}-${POD_INDEX}-pc-p2p is ${PARA_CHAIN_P2P_PORT}" + + {{- if and (.Values.node.perNodeServices.createP2pService) (.Values.node.collator.isParachain) (eq .Values.node.perNodeServices.p2pServiceType "Nodeport") }} + PARA_CHAIN_P2P_PORT=$(kubectl --namespace {{ .Release.Namespace }} get service {{ $fullname }}-${POD_INDEX}-pc-p2p -o jsonpath='{.spec.ports[*].nodePort}') + {{- else if and (.Values.node.collator.isParachain) (or (eq .Values.node.perNodeServices.p2pServiceType "LoadBalancer") (eq .Values.node.perNodeServices.p2pServiceType "ClusterIP")) }} + PARA_CHAIN_P2P_PORT=30334 # Default port {{- end }} - {{- if and .Values.node.perNodeServices.setPublicAddressToExternal.enabled (eq .Values.node.perNodeServices.p2pServiceType "NodePort") }} - EXTERNAL_ADDRESS=$(curl -sS {{ .Values.node.perNodeServices.setPublicAddressToExternal.ipRetrievalServiceUrl }}) - echo -n "${EXTERNAL_ADDRESS}" > /data/node_external_address - echo "Retrieved external IP from {{ .Values.node.perNodeServices.setPublicAddressToExternal.ipRetrievalServiceUrl }}, saved ${EXTERNAL_ADDRESS} to /data/node_external_address" + + {{- if and (.Values.node.perNodeServices.createP2pService) (.Values.node.perNodeServices.setPublicAddressToExternal.enabled) (eq .Values.node.perNodeServices.p2pServiceType "NodePort") }} + EXTERNAL_ADDRESS=$(curl -sS {{ .Values.node.perNodeServices.setPublicAddressToExternal.ipRetrievalServiceUrl }}) {{- else if and .Values.node.perNodeServices.setPublicAddressToExternal.enabled (eq .Values.node.perNodeServices.p2pServiceType "LoadBalancer") }} - EXTERNAL_ADDRESS=$(kubectl --namespace {{ .Release.Namespace }} get service {{ $fullname }}-${POD_INDEX}-rc-p2p -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') - echo -n "${EXTERNAL_ADDRESS}" > /data/node_external_address - echo "External hostname is ${EXTERNAL_ADDRESS}, saved to /data/node_external_address" + EXTERNAL_ADDRESS=$(kubectl --namespace {{ .Release.Namespace }} get service {{ $fullname }}-${POD_INDEX}-rc-p2p -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') {{- else if eq .Values.node.perNodeServices.p2pServiceType "ClusterIP" }} - EXTERNAL_ADDRESS={{ $fullname }}-${POD_INDEX}-rc-p2p.{{ .Release.Namespace }}.svc.cluster.local - echo -n "${EXTERNAL_ADDRESS}" > /data/node_external_address - echo "External hostname is ${EXTERNAL_ADDRESS}, saved to /data/node_external_address" + EXTERNAL_ADDRESS={{ $fullname }}-${POD_INDEX}-rc-p2p.{{ .Release.Namespace }}.svc.cluster.local + EXTERNAL_P2P_PORT="{{ .Values.node.ports.p2p }}" + {{- else if eq .Values.proxy.provider "ambassador" }} + EXTERNAL_ADDRESS="{{ .Values.proxy.external_url }}" + EXTERNAL_P2P_PORT="{{ .Values.proxy.p2p }}" {{- end }} - volumeMounts: - - mountPath: /data - name: chain-data - {{- end }} - containers: - - name: {{ .Values.node.chain }} - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} - imagePullPolicy: {{ .Values.image.pullPolicy }} - command: ["/bin/sh", "-c"] - args: - - |- - #!/bin/sh - {{- if .Values.node.perNodeServices.createP2pService }} - if [ ! -s /data/node_external_address ]; then echo "EXTERNAL_ADDRESS is empty" && exit 1 ; fi - EXTERNAL_ADDRESS="$(cat /data/node_external_address)" - echo "EXTERNAL_ADDRESS=${EXTERNAL_ADDRESS}" - RELAY_CHAIN_P2P_PORT="$(cat /data/relay_chain_p2p_port)" - echo "RELAY_CHAIN_P2P_PORT=${RELAY_CHAIN_P2P_PORT}" - {{- if eq .Values.proxy.provider "ambassador" }} - EXTERNAL_ADDRESS="{{ .Values.proxy.external_url }}" - echo "EXTERNAL_ADDRESS=${EXTERNAL_ADDRESS}" - EXTERNAL_P2P_PORT="{{ .Values.proxy.p2p }}" - echo "EXTERNAL_P2P_PORT=${EXTERNAL_P2P_PORT}" + + echo "PARA_CHAIN_P2P_PORT:$PARA_CHAIN_P2P_PORT" + echo "RELAY_CHAIN_P2P_PORT:$RELAY_CHAIN_P2P_PORT" + echo "EXTERNAL_ADDRESS:$EXTERNAL_ADDRESS" + echo "EXTERNAL_P2P_PORT:$EXTERNAL_P2P_PORT" + + # STEP-7 + echo "Step 7: Start the node" + exec {{ .Values.node.command }} \ + --name=${POD_NAME} \ + --base-path=/data/ \ + --node-key=${NODE_KEY} \ + --chain={{ if .Values.node.customChainspecUrl }}{{ .Values.node.customChainspecPath }}{{ else }}${CHAIN}{{ end }} \ + {{- if or (eq .Values.node.role "authority") (eq .Values.node.role "validator") }} + --validator \ + {{- else if eq .Values.node.role "collator" }} + --collator \ + {{- else if or (eq .Values.node.role "light") (eq .Values.node.role "member") }} + --light \ {{- end }} - {{- if .Values.node.collator.isParachain }} - PARA_CHAIN_P2P_PORT="$(cat /data/para_chain_p2p_port)" - echo "PARA_CHAIN_P2P_PORT=${PARA_CHAIN_P2P_PORT}" + {{- if .Values.node.isBootnode.enabled }} + --bootnodes "/dns4/{{ .Values.node.isBootnode.bootnodeAddr }}/tcp/{{ .Values.node.isBootnode.bootnodePort }}/p2p/${BOOTNODE_ID}" \ {{- end }} + {{- if (.Values.node.collator.isParachain) }} + --base-path=/data/relay/ \ + --public-addr=/dns4/${EXTERNAL_ADDRESS}/tcp/${PARA_CHAIN_P2P_PORT} \ + --listen-addr=/ip4/0.0.0.0/tcp/${PARA_CHAIN_P2P_PORT} \ + {{- else if (.Values.node.perNodeServices.createP2pService) }} + --public-addr=/dns4/${EXTERNAL_ADDRESS}/tcp/${EXTERNAL_P2P_PORT} \ + --listen-addr=/ip4/0.0.0.0/tcp/${RELAY_CHAIN_P2P_PORT} \ {{- end }} - exec {{ .Values.node.command }} \ - --name=${POD_NAME} \ - --base-path=/data/ \ - --chain={{ if .Values.node.customChainspecUrl }}{{ .Values.node.customChainspecPath }}{{ else }}${CHAIN}{{ end }} \ - {{- if or (eq .Values.node.role "authority") (eq .Values.node.role "validator") }} - --validator \ - {{- end }} - {{- if eq .Values.node.role "collator" }} - --collator \ - {{- end }} - {{- if or (eq .Values.node.role "light") (eq .Values.node.role "member") }} - --light \ - {{- end }} - {{- if .Values.node.collator.isParachain }} - {{- if .Values.node.perNodeServices.createP2pService }} - {{- if .Values.node.perNodeServices.setPublicAddressToExternal.enabled }} - {{- if eq .Values.node.perNodeServices.p2pServiceType "NodePort" }} - --public-addr=/ip4/${EXTERNAL_ADDRESS}/tcp/${PARA_CHAIN_P2P_PORT} \ - {{- else if eq .Values.node.perNodeServices.p2pServiceType "LoadBalancer" }} - --public-addr=/dns4/${EXTERNAL_ADDRESS}/tcp/${PARA_CHAIN_P2P_PORT} \ - {{- end }} - {{- else if and (not .Values.node.perNodeServices.setPublicAddressToExternal.enabled) (eq .Values.node.perNodeServices.p2pServiceType "ClusterIP") }} - --public-addr=/dns4/${EXTERNAL_ADDRESS}/tcp/${PARA_CHAIN_P2P_PORT} \ - {{- end }} - --listen-addr=/ip4/0.0.0.0/tcp/${PARA_CHAIN_P2P_PORT} \ - {{- end }} - --listen-addr=/ip4/0.0.0.0/tcp/30334 \ - {{- end }} - {{- if .Values.node.persistGeneratedNodeKey }} - --node-key-file /data/node-key \ - {{- else if .Values.node.customNodeKey }} - --node-key $(cat /tmp/custom-node-key) \ - {{- else if .Values.vault.secretPrefix }} - --node-key $(cat /secrets/node_key) \ - {{- end }} - {{- if .Values.node.tracing.enabled }} - --jaeger-agent=127.0.0.1:{{ .Values.jaegerAgent.ports.compactPort }} \ - {{- end }} - {{- join " " .Values.node.flags | nindent 16 }} \ - {{- if .Values.node.collator.isParachain }} - -- \ - --base-path=/data/relay/ \ - {{- end }} - {{- if .Values.node.collator.relayChainCustomChainspecUrl }} - --chain={{ .Values.node.relayChainCustomChainspecPath }} \ - {{- end }} - {{- if .Values.node.perNodeServices.createP2pService }} - {{- if .Values.node.perNodeServices.setPublicAddressToExternal.enabled }} - {{- if eq .Values.node.perNodeServices.p2pServiceType "NodePort" }} - --public-addr=/ip4/${EXTERNAL_ADDRESS}/tcp/${RELAY_CHAIN_P2P_PORT} \ - {{- else if eq .Values.node.perNodeServices.p2pServiceType "LoadBalancer" }} - --public-addr=/dns4/${EXTERNAL_ADDRESS}/tcp/${RELAY_CHAIN_P2P_PORT} \ - {{- end }} - {{- else if and (not .Values.node.perNodeServices.setPublicAddressToExternal.enabled) (eq .Values.node.perNodeServices.p2pServiceType "ClusterIP") }} - --public-addr=/dns4/${EXTERNAL_ADDRESS}/tcp/${EXTERNAL_P2P_PORT} \ - {{- end }} - --listen-addr=/ip4/0.0.0.0/tcp/${RELAY_CHAIN_P2P_PORT} \ - {{- else }} - --listen-addr=/ip4/0.0.0.0/tcp/30333 \ - {{- end }} - {{- join " " .Values.node.collator.relayChainFlags | nindent 16 }} + {{- if .Values.node.tracing.enabled }} + --jaeger-agent=127.0.0.1:{{ .Values.jaegerAgent.ports.compactPort }} \ + {{- end }} + {{- if .Values.node.collator.relayChainCustomChainspecUrl }} + --chain={{ .Values.node.relayChainCustomChainspecPath }} \ + {{- end }} + {{- join " " .Values.node.flags | nindent 16 }} \ + {{- join " " .Values.node.collator.relayChainFlags | nindent 16 }} env: - name: CHAIN value: {{ .Values.node.chain }} @@ -551,6 +368,9 @@ spec: name: custom-node-key readOnly: true {{- end }} + - name: package-manager + mountPath: /scripts/package-manager.sh + subPath: package-manager.sh {{- if .Values.node.substrateApiSidecar.enabled }} - name: substrate-api-sidecar image: {{ .Values.substrateApiSidecar.image.repository }}:{{ .Values.substrateApiSidecar.image.tag }} @@ -604,8 +424,6 @@ spec: {{- toYaml . | nindent 8 }} {{- end}} serviceAccountName: {{ $serviceAccountName }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} {{- with .Values.nodeSelector }} nodeSelector: @@ -640,6 +458,9 @@ spec: - name: keystore emptyDir: medium: Memory + - name: package-manager + configMap: + name: package-manager volumeClaimTemplates: - apiVersion: v1 kind: PersistentVolumeClaim @@ -653,7 +474,7 @@ spec: kind: VolumeSnapshot apiGroup: snapshot.storage.k8s.io {{- end }} - storageClassName: {{ .Values.storageClass }} + storageClassName: storage-{{ .Release.Name }} resources: requests: storage: {{ .Values.node.dataVolumeSize }} diff --git a/platforms/substrate/charts/substrate-node/values.yaml b/platforms/substrate/charts/substrate-node/values.yaml index 53fd45b2fe5..4b2647b0072 100644 --- a/platforms/substrate/charts/substrate-node/values.yaml +++ b/platforms/substrate/charts/substrate-node/values.yaml @@ -1,12 +1,47 @@ + +############################################################################################## +# Copyright Accenture. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################################################## + # This is a YAML-formatted file. # Declare variables to be passed into your templates. +--- +global: + #Provide the service account name which will be created. + serviceAccountName: vault-auth + cluster: + provider: azure # choose from: minikube | aws + cloudNativeServices: false # 'false' is implemented + #Provide the kubernetes host url + #Eg. kubernetesUrl: https://10.3.8.5:6443 + kubernetesUrl: + vault: + # Provide the type of vault + type: "" # hashicorp + # Provide the vault role used. + role: vault-role + # Provide the network type + network: substrate + # Provide the vault server address + address: "" + # Provide the vault authPath configured to be used. + authPath: supplychain + # Provide the secret engine. + secretEngine: secretsv2 + # Provide the vault path where the secrets will be stored + secretPrefix: "data/supplychain" + # substrate node runtime image # Eg. repository: parity/substrate image: - repository: substrate - tag: 3.0.0 + repository: ghcr.io/inteli-poc/dscp-node + tag: v4.3.1 pullPolicy: IfNotPresent +# imagePullSecrets: +# - name: "regcred" # image for downloading chain snapshot - 7-Zip Docker image based on Alpine Linux. # Eg. repository: crazymax/7zip @@ -26,11 +61,9 @@ googleCloudSdk: image: repository: google/cloud-sdk tag: slim # more lightweight than the full image and still contains gsutil - #serviceAccountKey: "" + # serviceAccountKey: "" imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" serviceAccount: # Specifies whether a service account should be created @@ -42,7 +75,7 @@ serviceAccount: # Provide the service account name authenticated to vault. # NOTE: Make sure that the service account is already created and authenticated to use the vault. # Eg. name: vault-auth - name: "" + name: vault-auth podSecurityContext: runAsUser: 1000 @@ -64,101 +97,100 @@ ingress: # hosts: # - chart-example.local -#extraLabels: +# extraLabels: # type: rpc # This section contains the Substrate node details. node: - # Provide the name of the node. - # Eg. name: example - name: + chainDataSnapshotUrl: false + chainDataGcsBucketUrl: false # Specifies the chain specification. It can be one of the predefined ones (dev, local, or staging) # or it can be a path to a file with the chainspec (such as one exported by the `build-spec` subcommand). # Eg. chain: local or chain: - chain: + chain: inteli # Substrate CLI executable # Eg. command: substrate - command: + command: ./dscp-node + isBootnode: + enabled: false # false | true + bootnodeName: + bootnodeAddr: + bootnodePort: 30333 # Provide the size of the volume # Eg. dataVolumeSize: 1Gi - dataVolumeSize: 100Gi + dataVolumeSize: 10Gi # Provide the number of replicas for your pods. # replicas: 1 replicas: 1 # role of the node # Eg. role: full - role: - #customChainspecUrl: - ## Node may require custom name for chainspec file. Example: moonbeam https://github.com/PureStake/moonbeam/issues/1104#issuecomment-996787548 - ## Note: path should start with /data/ since this folder mount in init container download-chainspec. - #Eg. customChainspecPath: "/data/chainspec.json" - customChainspecPath: "" - #chainDataSnapshotUrl: "https://dot-rocksdb.polkashots.io/snapshot" - #chainDataSnapshotFormat: 7z + role: validator # full | validator + customChainspecUrl: true + # Node may require custom name for chainspec file. Example: moonbeam https://github.com/PureStake/moonbeam/issues/1104#issuecomment-996787548 + # Note: path should start with /data/ since this folder mount in init container download-chainspec. + # Eg. customChainspecPath: "/data/chainspec.json" + customChainspecPath: "/data/chainspec.json" + # chainDataSnapshotUrl: "https://dot-rocksdb.polkashots.io/snapshot" + # chainDataSnapshotFormat: 7z # Specifies the directory for storing all of the data related to this chain. # Eg. chainPath: /tmp/alice chainPath: "" - #chainDataKubernetesVolumeSnapshot: "" - #chainDataGcsBucketUrl: "" + # chainDataKubernetesVolumeSnapshot: "" + # chainDataGcsBucketUrl: "" collator: isParachain: false - relayChain: polkadot - # relayChainCustomChainspecUrl: "" - # relayChainCustomChainspecPath: "/data/relay_chain_chainspec.json" - # relayChainDataSnapshotUrl: "https://dot-rocksdb.polkashots.io/snapshot" - # relayChainDataSnapshotFormat: 7z - # relayChainPath: "" - # relayChainDataKubernetesVolumeSnapshot: "" - # relayChainDataGcsBucketUrl: "" - # relayChainFlags: - enableStartupProbe: - enableReadinessProbe: + # relayChainCustomChainspecUrl: "" + # relayChainCustomChainspecPath: "/data/relay_chain_chainspec.json" + # relayChainDataSnapshotUrl: "https://dot-rocksdb.polkashots.io/snapshot" + # relayChainDataSnapshotFormat: 7z + # relayChainPath: "" + # relayChainDataKubernetesVolumeSnapshot: "" + # relayChainDataGcsBucketUrl: "" + # relayChainFlags: + + enableStartupProbe: false + enableReadinessProbe: false flags: - # - "--rpc-external" - # - "--ws-external" - # - "--rpc-methods=safe" - # - "--rpc-cors=all" - # - "--prometheus-external" - keys: {} - # - type: "gran" - # scheme: "ed25519" - # seed: "//Alice//gran" - # - type: "babe" - # scheme: "sr25519" - # seed: "//Alice//babe" + - "--rpc-external" + - "--ws-external" + - "--rpc-methods=Unsafe" + - "--rpc-cors=all" + - "--unsafe-ws-external" + - "--unsafe-rpc-external" + persistGeneratedNodeKey: false - #customNodeKey: "" + # customNodeKey: "" resources: {} serviceMonitor: enabled: false - #namespace: monitoring - #interval: 10s + # namespace: monitoring + # interval: 10s # scrapeTimeout: 10s + perNodeServices: createApiService: true createP2pService: true - p2pServiceType: ClusterIP # Must be type ClusterIP, NodePort or LoadBalancer, If using type NodePort or LoadBalancer then you must set NodeSelecter accordingly. - relayServiceAnnotations: {} - paraServiceAnnotations: {} + p2pServiceType: ClusterIP # Must be type ClusterIP, NodePort or LoadBalancer, If using type NodePort or LoadBalancer then you must set NodeSelecter accordingly. setPublicAddressToExternal: enabled: false - ipRetrievalServiceUrl: https://ifconfig.io/ip - #podManagementPolicy: Parallel + ipRetrievalServiceUrl: https://ifconfig.io + podManagementPolicy: Parallel + ports: - # Specifies the port to listen on for peer-to-peer (p2p) traffic - # Eg. p2p: 30333 - p2p: - # Specifies the port to listen on for incoming WebSocket traffic - # Eg. ws: 9944 - ws: - # Specifies the port to listen on for incoming RPC traffic - # Eg. 9933 - rpc: + # Specifies the port to listen on for peer-to-peer (p2p) traffic + # Eg. p2p: 30333 + p2p: 30333 + # Specifies the port to listen on for incoming WebSocket traffic + # Eg. ws: 9944 + ws: 9944 + # Specifies the port to listen on for incoming RPC traffic + # Eg. 9933 + rpc: 9933 # Enables Jaeger Agent as a sidecar tracing: enabled: false - + # Enables Sustrate API as a sidecar substrateApiSidecar: enabled: false @@ -167,14 +199,14 @@ node: proxy: # Mention the proxy provider. Currently ambassador is supported # eg. provider: ambassador - provider: ambassador + provider: none # none | ambassador # url that will be added in DNS recordset # eg. external_url: test.substrate.example.com - external_url: + external_url: # Mention the p2p port configured on proxy. # NOTE: Make sure that the port is enabled and not binded on the proxy. # Eg. p2p: 15010 - p2p: + p2p: # Provide the secret name which contains the certificate certSecret: @@ -215,29 +247,4 @@ tolerations: [] affinity: {} -# Provide the name of the storageclass. -# NOTE: Make sure that the storageclass exist prior to this deployment as -# this chart doesn't create the storageclass. -# Eg. storageClass: gcpstorageclass -storageClass: - extraContainers: [] - -# This section contains the vault related information. -# NOTE: Make sure that the vault is already unsealed, intialized and configured to -# use the Kubernetes service account token based authentication. -vault: - # Provide the vault address - # Eg. address: http://vault.example.com:8200 - address: - # Provide the vault path where the secrets are stored - # Eg. secretPrefix: secretsv2/sub-org-name - secretPrefix: - # Provide the auth path configured to be used. Default is /kubernetes - authPath: - # Provide the vault role used. - # Eg. appRole: vault-role - appRole: - # NOTE: The alpine image used is the base alpine image with CURL installed. - # Eg. image: ghcr.io/hyperledger/bevel-alpine:latest - image: diff --git a/platforms/substrate/charts/values/noproxy-and-novault/genesis.yaml b/platforms/substrate/charts/values/noproxy-and-novault/genesis.yaml new file mode 100644 index 00000000000..b1d32f2c6c1 --- /dev/null +++ b/platforms/substrate/charts/values/noproxy-and-novault/genesis.yaml @@ -0,0 +1,25 @@ +############################################################################################## +# Copyright Accenture. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################################################## + +--- +# helm install genesis -f values/noproxy-and-novault/genesis.yaml -n supplychain-subs substrate-genesis + +# The following are for overriding global values +global: + serviceAccountName: vault-auth + cluster: + provider: azure + cloudNativeServices: false + vault: + # Provide the type of vault + type: kubernetes # hashicorp | kubernetes + +node: + validator: + count: 4 + member: + count: 1 + balance: 1152921504606846976 diff --git a/platforms/substrate/charts/values/noproxy-and-novault/ipfs.yaml b/platforms/substrate/charts/values/noproxy-and-novault/ipfs.yaml new file mode 100644 index 00000000000..b7c629f5a89 --- /dev/null +++ b/platforms/substrate/charts/values/noproxy-and-novault/ipfs.yaml @@ -0,0 +1,19 @@ +############################################################################################## +# Copyright Accenture. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################################################## + +global: + serviceAccountName: vault-auth + cluster: + provider: azure + cloudNativeServices: false + vault: + type: kubernetes # kubernetes | hashicorp + +proxy: + provider: none # none | ambassador + +config: + nodeHost: diff --git a/platforms/substrate/charts/values/noproxy-and-novault/node.yaml b/platforms/substrate/charts/values/noproxy-and-novault/node.yaml new file mode 100644 index 00000000000..9f81c7da46a --- /dev/null +++ b/platforms/substrate/charts/values/noproxy-and-novault/node.yaml @@ -0,0 +1,25 @@ +############################################################################################## +# Copyright Accenture. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +############################################################################################## + +--- +# helm install validator-1 -f values/noproxy-and-novault/node.yaml -n supplychain-subs substrate-node +# helm upgrade validator-1 -f values/noproxy-and-novault/node.yaml -n supplychain-subs substrate-node + +global: + serviceAccountName: vault-auth + cluster: + provider: azure + cloudNativeServices: false + vault: + type: kubernetes # hashicorp | kubernetes + +node: + isBootnode: + enabled: true # false | true + bootnodeName: + bootnodeAddr: + bootnodePort: 30333 + role: validator # full | validator