@starship-ci/generator
Version:
Kubernetes manifest generator for Starship deployments
384 lines (377 loc) • 16 kB
JavaScript
"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.NeutronQueryRelayerBuilder = exports.NeutronQueryStatefulSetGenerator = exports.NeutronQueryServiceGenerator = exports.NeutronQueryConfigMapGenerator = void 0;
const helpers = __importStar(require("../../helpers"));
const version_1 = require("../../version");
const base_1 = require("./base");
/**
* ConfigMap generator for Neutron Query Relayer
*/
class NeutronQueryConfigMapGenerator {
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}`
}
};
return [
{
apiVersion: 'v1',
kind: 'ConfigMap',
metadata,
data: {
'config.json': this.generateNeutronQueryConfig()
}
}
];
}
generateNeutronQueryConfig() {
const relayerConfig = this.relayer.config || {};
// Find the neutron chain (should be the first one typically)
const neutronChainId = this.relayer.chains.find((chainId) => {
const chain = this.config.chains.find((c) => String(c.id) === chainId);
return chain?.name === 'neutron';
}) || this.relayer.chains[0];
const neutronChain = this.config.chains.find((c) => String(c.id) === neutronChainId);
if (!neutronChain) {
throw new Error(`Neutron chain ${neutronChainId} not found in configuration`);
}
const neutronChainName = helpers.getChainName(String(neutronChain.id));
// Find the target chain (should be the second one typically)
const targetChainId = this.relayer.chains.find((chainId) => chainId !== neutronChainId) ||
this.relayer.chains[1];
const targetChain = this.config.chains.find((c) => String(c.id) === targetChainId);
if (!targetChain) {
throw new Error(`Target chain ${targetChainId} not found in configuration`);
}
const targetChainName = helpers.getChainName(String(targetChain.id));
const config = {
relayer: {
neutron_chain: {
chain_id: neutronChainId,
rpc_addr: `http://${neutronChainName}-genesis.$(NAMESPACE).svc.cluster.local:26657`,
grpc_addr: `http://${neutronChainName}-genesis.$(NAMESPACE).svc.cluster.local:9090`,
websocket_addr: `ws://${neutronChainName}-genesis.$(NAMESPACE).svc.cluster.local:26657/websocket`,
account_prefix: neutronChain.prefix,
keyring_backend: 'test',
gas_prices: `${relayerConfig.neutron_gas_prices || '0.025'}${neutronChain.denom}`,
gas_adjustment: relayerConfig.neutron_gas_adjustment || 1.5,
connection_id: relayerConfig.neutron_connection_id || 'connection-0',
debug: relayerConfig.debug || false,
timeout: relayerConfig.timeout || '10s',
tx_memo: relayerConfig.tx_memo || 'neutron-query-relayer'
},
target_chain: {
chain_id: targetChainId,
rpc_addr: `http://${targetChainName}-genesis.$(NAMESPACE).svc.cluster.local:26657`,
grpc_addr: `http://${targetChainName}-genesis.$(NAMESPACE).svc.cluster.local:9090`,
websocket_addr: `ws://${targetChainName}-genesis.$(NAMESPACE).svc.cluster.local:26657/websocket`,
account_prefix: targetChain.prefix,
keyring_backend: 'test',
gas_prices: `${relayerConfig.target_gas_prices || '0.025'}${targetChain.denom}`,
gas_adjustment: relayerConfig.target_gas_adjustment || 1.5,
connection_id: relayerConfig.target_connection_id || 'connection-0',
debug: relayerConfig.debug || false,
timeout: relayerConfig.timeout || '10s',
tx_memo: relayerConfig.tx_memo || 'neutron-query-relayer'
},
queries_file: relayerConfig.queries_file || '/configs/queries.json',
check_submitted_tx: relayerConfig.check_submitted_tx !== false,
storage_path: relayerConfig.storage_path || './storage',
log_level: relayerConfig.log_level || 'info'
}
};
return JSON.stringify(config, null, 2);
}
}
exports.NeutronQueryConfigMapGenerator = NeutronQueryConfigMapGenerator;
/**
* Service generator for Neutron Query Relayer
*/
class NeutronQueryServiceGenerator {
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 ports = [
{
name: 'metrics',
port: this.relayer.config?.metrics_port || 9090,
protocol: 'TCP',
targetPort: this.relayer.config?.metrics_port || 9090
}
];
return [
{
apiVersion: 'v1',
kind: 'Service',
metadata,
spec: {
clusterIP: 'None',
ports,
selector: {
'app.kubernetes.io/name': `${this.relayer.type}-${this.relayer.name}`
}
}
}
];
}
}
exports.NeutronQueryServiceGenerator = NeutronQueryServiceGenerator;
/**
* StatefulSet generator for Neutron Query Relayer
*/
class NeutronQueryStatefulSetGenerator {
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 neutron-query-relayer init container
initContainers.push(this.generateNeutronQueryInitContainer());
return initContainers;
}
generateNeutronQueryInitContainer() {
const image = this.relayer.image ||
'ghcr.io/cosmology-tech/starship/neutron-query-relayer:v0.2.0';
const env = this.generateEnvironmentVariables();
const command = this.generateNeutronQueryInitCommand();
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 neutron-query-relayer container
containers.push({
name: 'relayer',
image: this.relayer.image ||
'ghcr.io/cosmology-tech/starship/neutron-query-relayer:v0.2.0',
imagePullPolicy: this.config.images?.imagePullPolicy || 'IfNotPresent',
env: this.generateEnvironmentVariables(),
command: ['bash', '-c'],
args: [
'RLY_INDEX=${HOSTNAME##*-}\necho "Relayer Index: $RLY_INDEX"\nneutron-query-relayer start --config /configs/config.json'
],
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' } }
];
}
generateEnvironmentVariables() {
const relayerConfig = this.relayer.config || {};
return [
{ name: 'KEYS_CONFIG', value: '/keys/keys.json' },
{ name: 'RELAYER_INDEX', value: '${HOSTNAME##*-}' },
{
name: 'NAMESPACE',
valueFrom: { fieldRef: { fieldPath: 'metadata.namespace' } }
},
{ name: 'CONFIG_PATH', value: '/configs/config.json' },
{
name: 'STORAGE_PATH',
value: relayerConfig.storage_path || './storage'
},
{ name: 'LOG_LEVEL', value: relayerConfig.log_level || 'info' },
{
name: 'METRICS_PORT',
value: String(relayerConfig.metrics_port || 9090)
}
];
}
generateNeutronQueryInitCommand() {
let command = `set -ux
RLY_INDEX=\${HOSTNAME##*-}
echo "Relayer Index: $RLY_INDEX"
mkdir -p $STORAGE_PATH
NEUTRON_MNEMONIC=$(jq -r ".relayers[$RLY_INDEX].mnemonic" $KEYS_CONFIG)
TARGET_MNEMONIC=$(jq -r ".relayers[$RLY_INDEX].mnemonic" $KEYS_CONFIG)
`;
// Add key creation and funding for each chain
this.relayer.chains.forEach((chainId) => {
const chain = this.config.chains.find((c) => String(c.id) === chainId);
if (!chain)
return;
command += `
echo "Setting up keys for ${chainId}..."
# Keys will be managed through environment variables for neutron-query-relayer
DENOM="${chain.denom}"
# For neutron-query-relayer, we need to derive the address from mnemonic
# This will be handled by the relayer binary itself
echo "Chain ${chainId} setup completed"
`;
});
return command;
}
}
exports.NeutronQueryStatefulSetGenerator = NeutronQueryStatefulSetGenerator;
/**
* Main Neutron Query Relayer builder
*/
class NeutronQueryRelayerBuilder extends base_1.BaseRelayerBuilder {
constructor(relayer, config) {
super(relayer, config);
this.generators = [
new NeutronQueryConfigMapGenerator(relayer, config),
new NeutronQueryServiceGenerator(relayer, config),
new NeutronQueryStatefulSetGenerator(relayer, config)
];
}
}
exports.NeutronQueryRelayerBuilder = NeutronQueryRelayerBuilder;