UNPKG

@starship-ci/generator

Version:

Kubernetes manifest generator for Starship deployments

374 lines (369 loc) 13.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EthereumStatefulSetGenerator = void 0; const helpers = __importStar(require("../../../helpers")); /** * Generates the StatefulSet for Ethereum chain * Based on the Helm template: chains/eth/statefulsets.yaml */ class EthereumStatefulSetGenerator { config; chain; constructor(chain, config) { this.config = config; this.chain = chain; } generate() { const name = `${this.chain.name}-${this.chain.id}`; return [ { apiVersion: 'apps/v1', kind: 'StatefulSet', metadata: { name: name, labels: { ...helpers.getCommonLabels(this.config), app: name, 'app.kubernetes.io/component': 'chain', 'app.kubernetes.io/name': name, 'app.kubernetes.io/part-of': helpers.getChainId(this.chain), 'app.kubernetes.io/role': 'ethereum', 'starship.io/chain-name': this.chain.name, 'starship.io/chain-id': helpers.getChainId(this.chain) } }, spec: { serviceName: name, replicas: 1, selector: { matchLabels: { 'app.kubernetes.io/instance': name, 'app.kubernetes.io/name': name } }, template: { metadata: { annotations: { quality: 'release', role: 'api-gateway', sla: 'high', tier: 'gateway' }, labels: { 'app.kubernetes.io/instance': name, 'app.kubernetes.io/type': name, 'app.kubernetes.io/name': name, 'app.kubernetes.io/rawname': String(this.chain.id) } }, spec: { initContainers: this.createInitContainers(this.chain), containers: this.createMainContainers(this.chain), volumes: this.createVolumes() } } } } ]; } createInitContainers(chain) { const initContainers = []; // Init Genesis Beacon container initContainers.push(this.createInitGenesisBeaconContainer(chain)); // Init Genesis Execution container initContainers.push(this.createInitGenesisExecutionContainer(chain)); return initContainers; } createInitGenesisBeaconContainer(chain) { const prysmctlImage = chain.config?.prysmctl?.image || 'ghcr.io/hyperweb-io/starship/prysm/cmd/prysmctl:v5.2.0'; const numValidators = chain.config?.validator?.numValidator || 1; return { name: 'init-genesis-beacon', image: prysmctlImage, imagePullPolicy: 'IfNotPresent', command: ['bash', '-c'], args: [ ` mkdir -p /ethereum/consensus /ethereum/execution cp /config/genesis.json /ethereum/execution/genesis.json cp /config/config.yaml /ethereum/consensus/config.yaml echo "Initializing genesis" prysmctl testnet generate-genesis \\ --fork=capella \\ --num-validators=${numValidators} \\ --genesis-time-delay=15 \\ --output-ssz=/ethereum/consensus/genesis.ssz \\ --chain-config-file=/ethereum/consensus/config.yaml \\ --geth-genesis-json-in=/ethereum/execution/genesis.json \\ --geth-genesis-json-out=/ethereum/execution/genesis.json echo "Copy secrets over" cp /config/jwt.hex /etc/secrets/jwt.hex `.trim() ], resources: this.getNodeResources(chain), volumeMounts: [ { name: 'secrets', mountPath: '/etc/secrets' }, { name: 'config', mountPath: '/config' }, { name: 'ethereum', mountPath: '/ethereum' } ] }; } createInitGenesisExecutionContainer(chain) { return { name: 'init-genesis-execution', image: chain.image, imagePullPolicy: 'IfNotPresent', command: ['bash', '-c'], args: [ ` echo "Initializing genesis geth" geth --datadir /ethereum/execution init /ethereum/execution/genesis.json `.trim() ], resources: this.getNodeResources(chain), volumeMounts: [ { name: 'secrets', mountPath: '/etc/secrets' }, { name: 'config', mountPath: '/config' }, { name: 'ethereum', mountPath: '/ethereum' } ] }; } createMainContainers(chain) { const containers = []; // Geth container containers.push(this.createGethContainer(chain)); // Beacon chain container containers.push(this.createBeaconChainContainer(chain)); // Validator container containers.push(this.createValidatorContainer(chain)); return containers; } createGethContainer(chain) { return { name: 'geth', image: chain.image, imagePullPolicy: 'IfNotPresent', env: [ { name: 'HTTP_PORT', value: '8545' }, { name: 'WS_PORT', value: '8546' }, { name: 'RPC_PORT', value: '8551' } ], command: ['bash', '-c'], args: [ ` echo "Setting UDP buffer size" sysctl -w net.core.rmem_max=16777216 sysctl -w net.core.wmem_max=16777216 echo "Starting execution chain" geth --datadir /ethereum/execution --http \\ --http.addr=0.0.0.0 \\ --http.port=$HTTP_PORT \\ --http.api=eth,net,web3,debug \\ --ws --ws.addr=0.0.0.0 \\ --ws.port=$WS_PORT \\ --authrpc.addr=0.0.0.0 \\ --authrpc.port=$RPC_PORT \\ --nodiscover \\ --http.corsdomain=* \\ --ws.api=eth,net,web3 \\ --ws.origins=* \\ --http.vhosts=* \\ --authrpc.vhosts=* \\ --authrpc.jwtsecret=/etc/secrets/jwt.hex \\ --unlock=0x123463a4B065722E99115D6c222f267d9cABb524 \\ --password=/dev/null \\ --syncmode=snap \\ --snapshot=false \\ --networkid=${chain.id} \\ --verbosity=4 \\ --maxpeers=50 \\ --nat=none \\ --log.vmodule=engine=6 `.trim() ], resources: this.getNodeResources(chain), volumeMounts: [ { name: 'ethereum', mountPath: '/ethereum' }, { name: 'secrets', mountPath: '/etc/secrets' } ], readinessProbe: { exec: { command: [ '/bin/bash', '-c', `curl -s --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' -H "Content-Type: application/json" -X POST http://localhost:8545 | grep -q '"result":false'` ] }, initialDelaySeconds: 15, periodSeconds: 10 } }; } createBeaconChainContainer(chain) { const beaconImage = chain.config?.beacon?.image || 'ghcr.io/hyperweb-io/starship/prysm/beacon-chain:v5.2.0'; return { name: 'beacon-chain', image: beaconImage, imagePullPolicy: 'Always', env: [ { name: 'NAMESPACE', valueFrom: { fieldRef: { fieldPath: 'metadata.namespace' } } } ], command: ['bash', '-c'], args: [ ` echo "Waiting 30 seconds for execution client to be ready..." sleep 30 echo "Starting consensus chain" beacon-chain \\ --execution-endpoint=http://0.0.0.0:8551 \\ --jwt-secret=/etc/secrets/jwt.hex \\ --accept-terms-of-use \\ --http-host 0.0.0.0 \\ --rpc-host 0.0.0.0 \\ --chain-id ${chain.id} \\ --contract-deployment-block=0 \\ --datadir /ethereum/consensus \\ --genesis-state /ethereum/consensus/genesis.ssz \\ --min-sync-peers=0 \\ --chain-config-file=/ethereum/consensus/config.yaml \\ --network-id ${chain.id} \\ --suggested-fee-recipient=0x123463a4B065722E99115D6c222f267d9cABb524 \\ --minimum-peers-per-subnet=0 \\ --force-clear-db `.trim() ], resources: this.getNodeResources(chain), volumeMounts: [ { name: 'ethereum', mountPath: '/ethereum' }, { name: 'secrets', mountPath: '/etc/secrets' } ], readinessProbe: { httpGet: { path: '/eth/v1/node/health', port: '3500' }, initialDelaySeconds: 15, periodSeconds: 20 } }; } createValidatorContainer(chain) { const validatorImage = chain.config?.validator?.image || 'ghcr.io/hyperweb-io/starship/prysm/validator:v5.2.0'; const numValidators = chain.config?.validator?.numValidator || 1; return { name: 'validator', image: validatorImage, imagePullPolicy: 'Always', env: [ { name: 'NAMESPACE', valueFrom: { fieldRef: { fieldPath: 'metadata.namespace' } } } ], command: ['bash', '-c'], args: [ ` echo "Waiting 15 seconds for execution client to be ready..." sleep 20 mkdir -p /ethereum/consensus/validator echo "Starting validator node" validator \\ --accept-terms-of-use \\ --beacon-rpc-provider=0.0.0.0:4000 \\ --datadir=/ethereum/consensus/validator \\ --interop-num-validators=${numValidators} \\ --interop-start-index=0 \\ --force-clear-db \\ --grpc-gateway-host=0.0.0.0 \\ --chain-config-file=/ethereum/consensus/config.yaml \\ --monitoring-host=0.0.0.0 \\ --monitoring-port=8081 \\ --suggested-fee-recipient=0x0C46c2cAFE097b4f7e1BB868B89e5697eE65f934 `.trim() ], resources: this.getNodeResources(chain), volumeMounts: [ { name: 'ethereum', mountPath: '/ethereum' }, { name: 'secrets', mountPath: '/etc/secrets' } ], readinessProbe: { httpGet: { path: '/metrics', port: '8081' }, initialDelaySeconds: 20, periodSeconds: 30 } }; } createVolumes() { return [ { name: 'config', configMap: { name: 'config-ethereum' } }, { name: 'ethereum', emptyDir: {} }, { name: 'secrets', emptyDir: {} } ]; } getNodeResources(chain) { // Use default resources or chain-specific resources const defaultResources = this.config.resources?.node; return { requests: { cpu: chain.resources?.cpu || defaultResources?.cpu, memory: chain.resources?.memory || defaultResources?.memory }, limits: { cpu: chain.resources?.cpu || defaultResources?.cpu, memory: chain.resources?.memory || defaultResources?.memory } }; } } exports.EthereumStatefulSetGenerator = EthereumStatefulSetGenerator;