UNPKG

@starship-ci/generator

Version:

Kubernetes manifest generator for Starship deployments

764 lines (745 loc) 29.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.MonitoringBuilder = exports.GrafanaDeploymentGenerator = exports.GrafanaServiceGenerator = exports.GrafanaConfigMapGenerator = exports.PrometheusDeploymentGenerator = exports.PrometheusServiceGenerator = exports.PrometheusRbacGenerator = exports.PrometheusConfigMapGenerator = void 0; const fs_1 = require("fs"); const path_1 = require("path"); const helpers = __importStar(require("../helpers")); /** * Prometheus generators for monitoring * Based on the Helm template: monitoring/prometheus.yaml */ class PrometheusConfigMapGenerator { config; constructor(config) { this.config = config; } generate() { if (!this.config.monitoring?.enabled) { return []; } const name = 'prometheus-config'; return [ { apiVersion: 'v1', kind: 'ConfigMap', metadata: { name, labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'monitoring', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/name': name } }, data: { 'prometheus.yml': this.generatePrometheusConfig() } } ]; } generatePrometheusConfig() { let config = `# my global config global: scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. # scrape_timeout is set to the global default (10s). scrape_configs: # The job name is added as a label \`job=<job_name>\` to any timeseries scraped from this config. - job_name: 'kubernetes-apiservers' kubernetes_sd_configs: - role: endpoints scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token relabel_configs: - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] action: keep regex: default;kubernetes;https - job_name: 'kubernetes-nodes' scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token kubernetes_sd_configs: - role: node relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+) - target_label: __address__ replacement: kubernetes.default.svc:443 - source_labels: [__meta_kubernetes_node_name] regex: (.+) target_label: __metrics_path__ replacement: /api/v1/nodes/\${1}/proxy/metrics - job_name: 'kubernetes-pods' kubernetes_sd_configs: - role: pod relabel_configs: - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] action: keep regex: true - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.+) - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] action: replace regex: ([^:]+)(?::\\d+)?;(\\d+) replacement: $1:$2 target_label: __address__ - action: labelmap regex: __meta_kubernetes_pod_label_(.+) - source_labels: [__meta_kubernetes_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_pod_name] action: replace target_label: kubernetes_pod_name - job_name: 'kube-state-metrics' static_configs: - targets: ['kube-state-metrics.kube-system.svc.cluster.local:8080'] - job_name: kubernetes-nodes-cadvisor scrape_interval: 10s scrape_timeout: 10s scheme: https # remove if you want to scrape metrics on insecure port tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token kubernetes_sd_configs: - role: node relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+) # Only for Kubernetes ^1.7.3. # See: https://github.com/prometheus/prometheus/issues/2916 - target_label: __address__ replacement: kubernetes.default.svc:443 - source_labels: [__meta_kubernetes_node_name] regex: (.+) target_label: __metrics_path__ replacement: /api/v1/nodes/\${1}/proxy/metrics/cadvisor metric_relabel_configs: - action: replace source_labels: [id] regex: '^/machine\\.slice/machine-rkt\\\\x2d([^\\\\]+)\\\\.+/([^/]+)\\.service$' target_label: rkt_container_name replacement: '\${2}-\${1}' - action: replace source_labels: [id] regex: '^/system\\.slice/(.+)\\.service$' target_label: systemd_service_name replacement: '\${1}' - job_name: 'kubernetes-service-endpoints' kubernetes_sd_configs: - role: endpoints relabel_configs: - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] action: keep regex: true - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] action: replace target_label: __scheme__ regex: (https?) - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.+) - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] action: replace target_label: __address__ regex: ([^:]+)(?::\\d+)?;(\\d+) replacement: $1:$2 - action: labelmap regex: __meta_kubernetes_service_label_(.+) - source_labels: [__meta_kubernetes_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_service_name] action: replace target_label: kubernetes_name - job_name: 'prometheus' # metrics_path defaults to '/metrics' # scheme defaults to 'http'. static_configs: - targets: ['localhost:9090'] `; // Add chain-specific monitoring jobs this.config.chains?.forEach((chain) => { if (chain.metrics) { const chainName = helpers.getChainName(String(chain.id)); // Genesis job config += ` - job_name: '${chain.name}-genesis' static_configs: - targets: ['${chainName}-genesis.$(NAMESPACE).svc.cluster.local:26660'] labels: instance: genesis type: genesis network: "${chain.name}" `; // Validator jobs if numValidators > 1 if (chain.numValidators && chain.numValidators > 1) { for (let i = 0; i < chain.numValidators - 1; i++) { config += ` - job_name: '${chain.name}-validator-${i}' static_configs: - targets: ['${chainName}-validator-${i}.${chainName}-validator.$(NAMESPACE).svc.cluster.local:26660'] labels: instance: "validator-${i}" type: validator network: "${chain.name}" `; } } } }); return config; } } exports.PrometheusConfigMapGenerator = PrometheusConfigMapGenerator; class PrometheusRbacGenerator { config; constructor(config) { this.config = config; } generate() { if (!this.config.monitoring?.enabled) { return []; } const name = 'prometheus'; return [ { apiVersion: 'rbac.authorization.k8s.io/v1', kind: 'ClusterRole', metadata: { name, labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'monitoring', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/name': name } }, rules: [ { apiGroups: [''], resources: [ 'nodes', 'nodes/proxy', 'services', 'endpoints', 'pods' ], verbs: ['get', 'list', 'watch'] }, { apiGroups: ['extensions'], resources: ['ingresses'], verbs: ['get', 'list', 'watch'] }, { nonResourceURLs: ['/metrics'], verbs: ['get'] } ] }, { apiVersion: 'rbac.authorization.k8s.io/v1', kind: 'ClusterRoleBinding', metadata: { name, labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'monitoring', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/name': name } }, roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'ClusterRole', name: 'prometheus' }, subjects: [ { kind: 'ServiceAccount', name: 'default', namespace: '$(NAMESPACE)' } ] } ]; } } exports.PrometheusRbacGenerator = PrometheusRbacGenerator; class PrometheusServiceGenerator { config; constructor(config) { this.config = config; } generate() { if (!this.config.monitoring?.enabled) { return []; } return [ { apiVersion: 'v1', kind: 'Service', metadata: { name: 'prometheus', labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'monitoring', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/name': 'prometheus' }, annotations: { 'prometheus.io/scrape': 'true', 'prometheus.io/port': '9090' } }, spec: { clusterIP: 'None', ports: [ { name: 'http', port: 9090, protocol: 'TCP', targetPort: '9090' } ], selector: { 'app.kubernetes.io/name': 'prometheus' } } } ]; } } exports.PrometheusServiceGenerator = PrometheusServiceGenerator; class PrometheusDeploymentGenerator { config; constructor(config) { this.config = config; } generate() { if (!this.config.monitoring?.enabled) { return []; } return [ { apiVersion: 'apps/v1', kind: 'Deployment', metadata: { name: 'prometheus', labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'monitoring', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/name': 'prometheus' } }, spec: { replicas: 1, selector: { matchLabels: { 'app.kubernetes.io/name': 'prometheus' } }, template: { metadata: { labels: { 'app.kubernetes.io/instance': 'monitoring', 'app.kubernetes.io/name': 'prometheus' }, annotations: { 'prometheus.io/scrape': 'true', 'prometheus.io/port': '9090' } }, spec: { containers: [ { name: 'prometheus', image: 'prom/prometheus', args: [ '--storage.tsdb.retention=6h', '--storage.tsdb.path=/prometheus', '--config.file=/etc/prometheus/prometheus.yml' ], ports: [ { name: 'web', containerPort: 9090 } ], resources: helpers.getResourceObject(this.config.monitoring?.resources || { cpu: '0.2', memory: '400M' }), volumeMounts: [ { name: 'prometheus-config-volume', mountPath: '/etc/prometheus' }, { name: 'prometheus-storage-volume', mountPath: '/prometheus' } ] } ], restartPolicy: 'Always', volumes: [ { name: 'prometheus-config-volume', configMap: { defaultMode: 420, name: 'prometheus-config' } }, { name: 'prometheus-storage-volume', emptyDir: {} } ] } } } } ]; } } exports.PrometheusDeploymentGenerator = PrometheusDeploymentGenerator; /** * Grafana generators for monitoring * Based on the Helm template: monitoring/grafana.yaml */ class GrafanaConfigMapGenerator { config; projectRoot; constructor(config, projectRoot = process.cwd()) { this.config = config; this.projectRoot = projectRoot; } loadGrafanaDashboards() { const dashboards = {}; const dashboardsDir = (0, path_1.join)(this.projectRoot, 'configs', 'grafana-dashboards'); try { const files = (0, fs_1.readdirSync)(dashboardsDir); if (!files.length) { throw new Error(`Expected to find Grafana dashboard configuration files in '${dashboardsDir}' but directory is empty. Please ensure dashboard JSON files are present.`); } files.forEach((file) => { if (file.endsWith('.json')) { const filePath = (0, path_1.join)(dashboardsDir, file); const content = (0, fs_1.readFileSync)(filePath, 'utf-8'); dashboards[file] = content; } }); if (Object.keys(dashboards).length === 0) { throw new Error(`Expected to find Grafana dashboard JSON files in '${dashboardsDir}' but no .json files were found. Please ensure dashboard configuration files are present.`); } } catch (error) { if (error instanceof Error && error.message.includes('Expected to find')) { throw error; // Re-throw our custom errors } throw new Error(`Failed to load Grafana dashboard configurations from '${dashboardsDir}'. Please ensure the directory exists and contains dashboard JSON files. Error: ${error}`); } return dashboards; } generate() { if (!this.config.monitoring?.enabled) { return []; } return [ { apiVersion: 'v1', kind: 'ConfigMap', metadata: { name: 'grafana-datasources', labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'monitoring', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/name': 'grafana-datasources' } }, data: { 'prometheus.yaml': JSON.stringify({ apiVersion: 1, datasources: [ { access: 'proxy', editable: true, name: 'prometheus', orgId: 1, type: 'prometheus', url: 'http://prometheus.aws-starship.svc:9090', version: 1 } ] }, null, 2) } }, { apiVersion: 'v1', kind: 'ConfigMap', metadata: { name: 'grafana-dashboard-providers', labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'monitoring', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/name': 'grafana-dashboard-providers' } }, data: { 'default.yaml': JSON.stringify({ apiVersion: 1, providers: [ { name: 'chain-dashboard', orgId: 1, type: 'file', allowUiUpdates: true, options: { path: '/var/lib/grafana/dashboards' } } ] }, null, 2) } }, { apiVersion: 'v1', kind: 'ConfigMap', metadata: { name: 'grafana-dashboards', labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'monitoring', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/name': 'grafana-dashboards' } }, data: this.loadGrafanaDashboards() } ]; } } exports.GrafanaConfigMapGenerator = GrafanaConfigMapGenerator; class GrafanaServiceGenerator { config; constructor(config) { this.config = config; } generate() { if (!this.config.monitoring?.enabled) { return []; } return [ { apiVersion: 'v1', kind: 'Service', metadata: { name: 'grafana', labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'monitoring', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/name': 'grafana' }, annotations: { 'prometheus.io/scrape': 'true', 'prometheus.io/port': '8080' } }, spec: { clusterIP: 'None', ports: [ { name: 'http', port: 8080, targetPort: 8080 } ], selector: { 'app.kubernetes.io/name': 'grafana' } } } ]; } } exports.GrafanaServiceGenerator = GrafanaServiceGenerator; class GrafanaDeploymentGenerator { config; constructor(config) { this.config = config; } generate() { if (!this.config.monitoring?.enabled) { return []; } return [ { apiVersion: 'apps/v1', kind: 'Deployment', metadata: { name: 'grafana', labels: { ...helpers.getCommonLabels(this.config), 'app.kubernetes.io/component': 'monitoring', 'app.kubernetes.io/part-of': 'starship', 'app.kubernetes.io/name': 'grafana' } }, spec: { replicas: 1, selector: { matchLabels: { 'app.kubernetes.io/name': 'grafana' } }, template: { metadata: { name: 'grafana', labels: { 'app.kubernetes.io/instance': 'monitoring', 'app.kubernetes.io/name': 'grafana' } }, spec: { containers: [ { name: 'grafana', image: 'grafana/grafana:latest', env: [ { name: 'GF_SERVER_HTTP_PORT', value: '8080' }, { name: 'GF_SERVER_HTTP_ADDR', value: '0.0.0.0' }, { name: 'GF_AUTH_DISABLE_LOGIN_FORM', value: 'true' }, { name: 'GF_AUTH_ANONYMOUS_ENABLED', value: 'true' }, { name: 'GF_AUTH_ANONYMOUS_ORG_NAME', value: 'Main Org.' }, { name: 'GF_AUTH_ANONYMOUS_ORG_ROLE', value: 'Editor' } ], ports: [ { name: 'grafana', containerPort: 3000 } ], resources: helpers.getResourceObject(this.config.monitoring.resources || { cpu: '0.2', memory: '400M' }), volumeMounts: [ { mountPath: '/var/lib/grafana', name: 'grafana-storage' }, { mountPath: '/etc/grafana/provisioning/datasources', name: 'grafana-datasources', readOnly: false }, { mountPath: '/etc/grafana/provisioning/dashboards', name: 'grafana-dashboard-providers', readOnly: false }, { mountPath: '/var/lib/grafana/dashboards', name: 'grafana-dashboards', readOnly: false } ] } ], volumes: [ { name: 'grafana-datasources', configMap: { defaultMode: 420, name: 'grafana-datasources' } }, { name: 'grafana-dashboard-providers', configMap: { defaultMode: 420, name: 'grafana-dashboard-providers' } }, { name: 'grafana-dashboards', configMap: { name: 'grafana-dashboards' } }, { name: 'grafana-storage', emptyDir: {} } ] } } } } ]; } } exports.GrafanaDeploymentGenerator = GrafanaDeploymentGenerator; /** * Main Monitoring builder * Orchestrates Prometheus and Grafana generation */ class MonitoringBuilder { config; generators; constructor(config, projectRoot = process.cwd()) { this.config = config; this.generators = []; if (this.config.monitoring?.enabled) { this.generators = [ // Prometheus new PrometheusRbacGenerator(config), new PrometheusConfigMapGenerator(config), new PrometheusServiceGenerator(config), new PrometheusDeploymentGenerator(config), // Grafana new GrafanaConfigMapGenerator(config, projectRoot), new GrafanaServiceGenerator(config), new GrafanaDeploymentGenerator(config) ]; } } generate() { return this.generators.flatMap((generator) => generator.generate()); } } exports.MonitoringBuilder = MonitoringBuilder;