UNPKG

@starship-ci/generator

Version:

Kubernetes manifest generator for Starship deployments

385 lines (376 loc) 15.5 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.GoRelayerBuilder = exports.GoRelayerStatefulSetGenerator = exports.GoRelayerConfigMapGenerator = void 0; const helpers = __importStar(require("../../helpers")); const version_1 = require("../../version"); const base_1 = require("./base"); /** * ConfigMap generator for Go Relayer */ class GoRelayerConfigMapGenerator { config; relayer; constructor(relayer, config) { this.config = config; this.relayer = relayer; } generate() { const metadata = { name: `${this.relayer.type}-${this.relayer.name}`, labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'relayer', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/role': this.relayer.type, 'app.kubernetes.io/name': `${this.relayer.type}-${this.relayer.name}` } }; const data = { 'path.json': this.generatePathConfig() }; // Generate individual chain configs this.relayer.chains.forEach((chainId) => { const chain = this.config.chains.find((c) => String(c.id) === chainId); if (!chain) { throw new Error(`Chain ${chainId} not found in configuration`); } data[`${chainId}.json`] = this.generateChainConfig(chainId, chain); }); return [ { apiVersion: 'v1', kind: 'ConfigMap', metadata, data } ]; } generatePathConfig() { const paths = {}; if (this.relayer.channels && this.relayer.channels.length > 0) { this.relayer.channels.forEach((channel, index) => { const pathName = `path${index}`; paths[pathName] = { src: { 'chain-id': channel['a-chain'], 'client-id': '', // Will be filled during connection creation 'connection-id': channel['a-connection'] || '', 'channel-id': '', // Will be filled during channel creation 'port-id': channel['a-port'] }, dst: { 'chain-id': channel['b-chain'] || '', 'client-id': '', // Will be filled during connection creation 'connection-id': '', // Will be filled during connection creation 'channel-id': '', // Will be filled during channel creation 'port-id': channel['b-port'] }, 'src-channel-filter': { rule: null, 'channel-list': [] } }; }); } else if (this.relayer.chains && this.relayer.chains.length >= 2) { // Generate a default path using the first two chains const srcChainId = this.relayer.chains[0]; const dstChainId = this.relayer.chains[1]; paths['path'] = { src: { 'chain-id': srcChainId, 'client-id': '', // Will be filled during connection creation 'connection-id': '', // Will be filled during connection creation 'channel-id': '', // Will be filled during channel creation 'port-id': 'transfer' }, dst: { 'chain-id': dstChainId, 'client-id': '', // Will be filled during connection creation 'connection-id': '', // Will be filled during connection creation 'channel-id': '', // Will be filled during channel creation 'port-id': 'transfer' }, 'src-channel-filter': { rule: null, 'channel-list': [] } }; } return JSON.stringify({ paths }, null, 2); } generateChainConfig(chainId, chain) { const chainName = helpers.getChainName(String(chain.id)); const relayerConfig = this.relayer.config || {}; const chainConfig = relayerConfig.chains?.find((c) => c.id === chainId) || {}; const config = { type: 'cosmos', value: { key: chainId, 'chain-id': chainId, 'rpc-addr': `http://${chainName}-genesis.$(NAMESPACE).svc.cluster.local:26657`, 'account-prefix': chainConfig.account_prefix || chain.prefix, 'keyring-backend': 'test', 'gas-adjustment': chainConfig.gas_adjustment || 1.2, 'gas-prices': `${chainConfig.gas_prices || '0.01'}${chain.denom}`, 'min-gas-amount': chainConfig.min_gas_amount || 0, debug: chainConfig.debug || false, timeout: chainConfig.timeout || '20s', 'block-timeout': chainConfig.block_timeout || '', 'output-format': 'json', 'sign-mode': 'direct', 'extra-codecs': chainConfig.extra_codecs || [] } }; return JSON.stringify(config, null, 2); } } exports.GoRelayerConfigMapGenerator = GoRelayerConfigMapGenerator; /** * StatefulSet generator for Go Relayer */ class GoRelayerStatefulSetGenerator { config; relayer; constructor(relayer, config) { this.config = config; this.relayer = relayer; } generate() { const fullname = `${this.relayer.type}-${this.relayer.name}`; return [ { apiVersion: 'apps/v1', kind: 'StatefulSet', metadata: { name: fullname, labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'relayer', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/role': this.relayer.type, 'app.kubernetes.io/name': fullname } }, spec: { serviceName: fullname, replicas: this.relayer.replicas || 1, podManagementPolicy: 'Parallel', revisionHistoryLimit: 3, selector: { matchLabels: { 'app.kubernetes.io/instance': 'relayer', 'app.kubernetes.io/type': this.relayer.type, 'app.kubernetes.io/name': fullname } }, template: { metadata: { annotations: { quality: 'release', role: 'api-gateway', sla: 'high', tier: 'gateway' }, labels: { 'app.kubernetes.io/instance': 'relayer', 'app.kubernetes.io/type': this.relayer.type, 'app.kubernetes.io/name': fullname, 'app.kubernetes.io/rawname': this.relayer.name, 'app.kubernetes.io/version': (0, version_1.getGeneratorVersion)() } }, spec: { initContainers: this.generateInitContainers(), containers: this.generateContainers(), volumes: this.generateVolumes() } } } } ]; } generateInitContainers() { const initContainers = []; // Add wait init containers for all chains this.relayer.chains.forEach((chainId) => { const chain = this.config.chains.find((c) => String(c.id) === chainId); if (!chain) return; const chainName = helpers.getChainName(String(chain.id)); initContainers.push({ name: `init-${chainName}`, image: 'ghcr.io/cosmology-tech/starship/wait-for-service:v0.1.0', imagePullPolicy: this.config.images?.imagePullPolicy || 'IfNotPresent', command: ['bash', '-c'], args: [ `echo "Waiting for ${chainName} service..."\nwait-for-service ${chainName}-genesis.$(NAMESPACE).svc.cluster.local:26657` ], env: [ { name: 'NAMESPACE', valueFrom: { fieldRef: { fieldPath: 'metadata.namespace' } } } ] }); }); // Add go-relayer init container initContainers.push(this.generateGoRelayerInitContainer()); return initContainers; } generateGoRelayerInitContainer() { const image = this.relayer.image || 'ghcr.io/cosmology-tech/starship/go-relayer:v2.4.1'; const env = [ { name: 'KEYS_CONFIG', value: '/keys/keys.json' }, { name: 'RELAYER_DIR', value: '/root/.relayer' }, { name: 'RELAYER_INDEX', value: '${HOSTNAME##*-}' }, { name: 'NAMESPACE', valueFrom: { fieldRef: { fieldPath: 'metadata.namespace' } } } ]; const command = this.generateGoRelayerInitCommand(); return { name: 'init-relayer', image, imagePullPolicy: this.config.images?.imagePullPolicy || 'IfNotPresent', env, command: ['bash', '-c'], args: [command], resources: helpers.getResourceObject(this.relayer.resources || { cpu: '0.2', memory: '200M' }), volumeMounts: [ { mountPath: '/root', name: 'relayer' }, { mountPath: '/configs', name: 'relayer-config' }, { mountPath: '/keys', name: 'keys' }, { mountPath: '/scripts', name: 'scripts' } ] }; } generateContainers() { const containers = []; // Main go-relayer container containers.push({ name: 'relayer', image: this.relayer.image || 'ghcr.io/cosmology-tech/starship/go-relayer:v2.4.1', imagePullPolicy: this.config.images?.imagePullPolicy || 'IfNotPresent', env: [{ name: 'RELAYER_DIR', value: '/root/.relayer' }], command: ['bash', '-c'], args: [ 'RLY_INDEX=${HOSTNAME##*-}\necho "Relayer Index: $RLY_INDEX"\nrly start' ], resources: helpers.getResourceObject(this.relayer.resources || { cpu: '0.2', memory: '200M' }), securityContext: { allowPrivilegeEscalation: false, runAsUser: 0 }, volumeMounts: [ { mountPath: '/root', name: 'relayer' }, { mountPath: '/configs', name: 'relayer-config' } ] }); return containers; } generateVolumes() { return [ { name: 'relayer', emptyDir: {} }, { name: 'relayer-config', configMap: { name: `${this.relayer.type}-${this.relayer.name}` } }, { name: 'keys', configMap: { name: 'keys' } }, { name: 'scripts', configMap: { name: 'setup-scripts' } } ]; } generateGoRelayerInitCommand() { let command = `set -ux RLY_INDEX=\${HOSTNAME##*-} echo "Relayer Index: $RLY_INDEX" mkdir -p $RELAYER_DIR/config cp /configs/path.json $RELAYER_DIR/config/ MNEMONIC=$(jq -r ".relayers[$RLY_INDEX].mnemonic" $KEYS_CONFIG) `; // Add chain configurations and key creation this.relayer.chains.forEach((chainId) => { const chain = this.config.chains.find((c) => String(c.id) === chainId); if (!chain) return; const chainName = helpers.getChainName(String(chain.id)); command += ` echo "Setting up chain ${chainId}..." cp /configs/${chainId}.json $RELAYER_DIR/config/ rly chains add --file /configs/${chainId}.json ${chainId} echo "Creating key for ${chainId}..." echo "$MNEMONIC" | rly keys restore ${chainId} ${chainId} --restore-key-type secp256k1 --coin-type 118 DENOM="${chain.denom}" RLY_ADDR=$(rly keys show ${chainId} ${chainId}) echo "Transfer tokens to address $RLY_ADDR" bash -e /scripts/transfer-tokens.sh \\ $RLY_ADDR \\ $DENOM \\ http://${chainName}-genesis.$NAMESPACE.svc.cluster.local:8000/credit \\ "${chain.faucet?.enabled || false}" || true `; }); // Add path setup and channel creation if specified if (this.relayer.channels && this.relayer.channels.length > 0) { command += ` echo "Adding paths..." rly paths add --file /configs/path.json `; this.relayer.channels.forEach((channel, index) => { const pathName = `path${index}`; if (channel['new-connection']) { command += ` echo "Creating client, connection and channel for ${pathName}..." rly tx link ${pathName} --src-port ${channel['a-port']} --dst-port ${channel['b-port']} `; } else { command += ` echo "Creating channel for ${pathName}..." rly tx channel ${pathName} --src-port ${channel['a-port']} --dst-port ${channel['b-port']} ${channel.order ? `--order ${channel.order}` : ''} `; } }); } return command; } } exports.GoRelayerStatefulSetGenerator = GoRelayerStatefulSetGenerator; /** * Main Go Relayer builder */ class GoRelayerBuilder extends base_1.BaseRelayerBuilder { constructor(relayer, config) { super(relayer, config); this.generators = [ new GoRelayerConfigMapGenerator(relayer, config), new GoRelayerStatefulSetGenerator(relayer, config) ]; } } exports.GoRelayerBuilder = GoRelayerBuilder;