diff --git a/.github/configs/ct.yaml b/.github/configs/ct.yaml index 7969d4cf..fd9a3bdf 100644 --- a/.github/configs/ct.yaml +++ b/.github/configs/ct.yaml @@ -6,7 +6,7 @@ target-branch: main chart-dirs: - charts chart-repos: [] -validate-chart-schema: false +validate-chart-schema: true validate-maintainers: true validate-yaml: true exclude-deprecated: true diff --git a/.github/workflows/helm-test.yml b/.github/workflows/helm-test.yml index 6b5db540..5ead1aae 100644 --- a/.github/workflows/helm-test.yml +++ b/.github/workflows/helm-test.yml @@ -3,11 +3,11 @@ name: CI # Controls when the workflow will run on: - # Triggers the workflow on push or pull request events but only for the "main" branch + # Triggers the workflow on push or pull request events for all branches push: - branches: [ "main" ] + branches: [ "**" ] pull_request: - branches: [ "main" ] + branches: [ "**" ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..d38ca760 --- /dev/null +++ b/Makefile @@ -0,0 +1,131 @@ +# OpenCost Helm Chart Makefile +# Provides targets for local development, testing, and CI validation + +.PHONY: help lint lint-default lint-ci lint-all template template-ci validate-schema test-all clean + +# Default target +help: + @echo "OpenCost Helm Chart - Available targets:" + @echo "" + @echo " make lint - Lint chart with default values" + @echo " make lint-ci - Lint chart with all CI test scenarios" + @echo " make lint-all - Lint default + all CI scenarios" + @echo " make template - Template chart with default values" + @echo " make template-ci - Template chart with all CI scenarios" + @echo " make validate-schema - Validate values.schema.json" + @echo " make test-all - Run all tests (lint + template + schema)" + @echo " make clean - Clean generated files" + @echo "" + +# Chart directory +CHART_DIR := charts/opencost +CI_DIR := $(CHART_DIR)/ci + +# Find all CI values files +CI_FILES := $(wildcard $(CI_DIR)/*.yaml) +CI_FILES := $(filter-out $(CI_DIR)/README.md,$(CI_FILES)) + +# Colors for output +RED := \033[0;31m +GREEN := \033[0;32m +YELLOW := \033[1;33m +NC := \033[0m # No Color + +# Lint chart with default values +lint-default: + @echo "$(YELLOW)Linting chart with default values...$(NC)" + @helm lint $(CHART_DIR) --strict + @echo "$(GREEN)✓ Default values lint passed$(NC)" + +# Lint chart with all CI scenarios +lint-ci: + @echo "$(YELLOW)Linting chart with CI scenarios...$(NC)" + @failed=0; \ + for file in $(CI_FILES); do \ + echo " Testing $$(basename $$file)..."; \ + if helm lint $(CHART_DIR) -f $$file --strict > /dev/null 2>&1; then \ + echo " $(GREEN)✓ PASSED$(NC)"; \ + else \ + echo " $(RED)✗ FAILED$(NC)"; \ + helm lint $(CHART_DIR) -f $$file --strict; \ + failed=$$((failed + 1)); \ + fi; \ + done; \ + if [ $$failed -eq 0 ]; then \ + echo "$(GREEN)✓ All CI scenarios passed$(NC)"; \ + else \ + echo "$(RED)✗ $$failed scenario(s) failed$(NC)"; \ + exit 1; \ + fi + +# Lint both default and CI scenarios +lint-all: lint-default lint-ci + @echo "$(GREEN)✓ All linting passed$(NC)" + +# Alias for lint-all +lint: lint-all + +# Template chart with default values +template-default: + @echo "$(YELLOW)Templating chart with default values...$(NC)" + @helm template opencost $(CHART_DIR) > /dev/null + @echo "$(GREEN)✓ Default template rendered successfully$(NC)" + +# Template chart with all CI scenarios +template-ci: + @echo "$(YELLOW)Templating chart with CI scenarios...$(NC)" + @failed=0; \ + for file in $(CI_FILES); do \ + echo " Templating $$(basename $$file)..."; \ + if helm template opencost $(CHART_DIR) -f $$file > /dev/null 2>&1; then \ + echo " $(GREEN)✓ PASSED$(NC)"; \ + else \ + echo " $(RED)✗ FAILED$(NC)"; \ + helm template opencost $(CHART_DIR) -f $$file; \ + failed=$$((failed + 1)); \ + fi; \ + done; \ + if [ $$failed -eq 0 ]; then \ + echo "$(GREEN)✓ All CI scenarios templated successfully$(NC)"; \ + else \ + echo "$(RED)✗ $$failed scenario(s) failed$(NC)"; \ + exit 1; \ + fi + +# Template both default and CI scenarios +template: template-default template-ci + @echo "$(GREEN)✓ All templating passed$(NC)" + +# Validate JSON schema +validate-schema: + @echo "$(YELLOW)Validating values.schema.json...$(NC)" + @if command -v python3 > /dev/null 2>&1; then \ + python3 -m json.tool $(CHART_DIR)/values.schema.json > /dev/null && \ + echo "$(GREEN)✓ Schema is valid JSON$(NC)"; \ + else \ + echo "$(YELLOW)⚠ python3 not found, skipping schema validation$(NC)"; \ + fi + +# Run all tests +test-all: validate-schema lint-all template + @echo "" + @echo "$(GREEN)========================================$(NC)" + @echo "$(GREEN)✓ All tests passed!$(NC)" + @echo "$(GREEN)========================================$(NC)" + +# Clean generated files (if any) +clean: + @echo "$(YELLOW)Cleaning generated files...$(NC)" + @rm -f $(CHART_DIR)/*.tgz + @echo "$(GREEN)✓ Clean complete$(NC)" + +# Quick test (lint only, faster for iteration) +quick: lint-default + @echo "$(GREEN)✓ Quick test passed$(NC)" + +# List all CI test files +list-ci: + @echo "CI test scenarios:" + @for file in $(CI_FILES); do \ + echo " - $$(basename $$file)"; \ + done \ No newline at end of file diff --git a/charts/opencost/Chart.yaml b/charts/opencost/Chart.yaml index 69a05f52..2d9f3272 100755 --- a/charts/opencost/Chart.yaml +++ b/charts/opencost/Chart.yaml @@ -9,7 +9,7 @@ keywords: - finops - monitoring - opencost -version: 2.5.23 +version: 2.5.24 maintainers: - name: jessegoodier - name: toscott diff --git a/charts/opencost/ci/README.md b/charts/opencost/ci/README.md new file mode 100644 index 00000000..02a94009 --- /dev/null +++ b/charts/opencost/ci/README.md @@ -0,0 +1,223 @@ +# OpenCost Helm Chart CI Test Values + +This directory contains test values files for CI/CD validation of the OpenCost Helm chart. Each file tests a specific configuration scenario to ensure the chart works correctly across different use cases. + +## Test Scenarios + +### 1. `mcp-enabled-values.yaml` +**Purpose:** Tests MCP (Model Context Protocol) server configuration +**Use Case:** AI agent integration for cost data access +**Key Features:** +- MCP server enabled on port 8081 +- Ingress configuration for MCP endpoint + +### 2. `external-prometheus-values.yaml` +**Purpose:** Tests external Prometheus configuration +**Use Case:** Using Grafana Cloud or other external Prometheus +**Key Features:** +- External Prometheus URL +- Basic authentication +- Internal Prometheus disabled + +### 3. `aws-amp-values.yaml` +**Purpose:** Tests AWS AMP (Amazon Managed Prometheus) integration +**Use Case:** AWS EKS clusters with AMP +**Key Features:** +- AMP workspace configuration +- SigV4 proxy for AWS authentication +- IAM role annotations + +### 4. `cloud-costs-enabled-values.yaml` +**Purpose:** Tests cloud cost integration +**Use Case:** Multi-cloud cost visibility +**Key Features:** +- Cloud cost ingestion enabled +- Cloud integration secret reference +- AWS credentials configuration + +### 5. `custom-pricing-values.yaml` +**Purpose:** Tests custom pricing configuration +**Use Case:** On-premises or custom pricing models +**Key Features:** +- Custom cost model +- ConfigMap creation +- Custom provider settings + +### 6. `ingress-enabled-values.yaml` +**Purpose:** Tests ingress configuration for UI and API +**Use Case:** External access to OpenCost +**Key Features:** +- UI ingress with TLS +- API ingress with TLS +- Cert-manager integration + +### 7. `persistence-enabled-values.yaml` +**Purpose:** Tests persistent storage configuration +**Use Case:** Data persistence and collector data source +**Key Features:** +- PVC configuration +- Collector data source enabled +- Custom storage class + +### 8. `plugins-enabled-values.yaml` +**Purpose:** Tests plugin installation and configuration +**Use Case:** Third-party integrations (e.g., Datadog) +**Key Features:** +- Plugin installation enabled +- Datadog plugin configuration +- Security context for plugin installer + +### 9. `high-availability-values.yaml` +**Purpose:** Tests HA configuration +**Use Case:** Production deployments requiring high availability +**Key Features:** +- Multiple replicas (3) +- Pod Disruption Budget +- Topology spread constraints +- Anti-affinity rules + +### 10. `servicemonitor-enabled-values.yaml` +**Purpose:** Tests Prometheus Operator ServiceMonitor +**Use Case:** Prometheus Operator-based monitoring +**Key Features:** +- ServiceMonitor creation +- Metric relabeling +- KSM metrics configuration +- Custom metrics config + +## Running Tests Locally + +### Quick Start with Makefile (Recommended) + +The repository includes a Makefile for easy local testing: + +```bash +# Show all available targets +make help + +# Quick test (lint default values only - fastest for iteration) +make quick + +# Lint all CI scenarios +make lint-ci + +# Lint default + all CI scenarios +make lint-all + +# Template all scenarios +make template + +# Run all tests (lint + template + schema validation) +make test-all + +# List all CI test files +make list-ci +``` + +### Manual Testing + +The chart uses [chart-testing (ct)](https://github.com/helm/chart-testing) which automatically discovers and tests all files in the `ci/` directory. + +#### Test Individual Scenarios + +```bash +# Lint a specific scenario +helm lint . -f ci/mcp-enabled-values.yaml + +# Template a specific scenario +helm template opencost . -f ci/mcp-enabled-values.yaml + +# Template and validate with kubectl +helm template opencost . -f ci/mcp-enabled-values.yaml | kubectl apply --dry-run=client -f - +``` + +#### Test All Scenarios with chart-testing + +```bash +# Install chart-testing +brew install chart-testing # macOS +# or follow: https://github.com/helm/chart-testing#installation + +# Run chart-testing lint (tests all ci/*.yaml files automatically) +ct lint --config .github/configs/ct.yaml --lint-conf .github/configs/lintconf.yaml --charts charts/opencost + +# Run chart-testing install (requires kind cluster) +kind create cluster +ct install --config .github/configs/ct.yaml --charts charts/opencost +``` + +#### Schema Validation + +Schema validation is enabled in the CI workflow via `validate-chart-schema: true` in `.github/configs/ct.yaml`. + +```bash +# Validate schema manually +make validate-schema + +# Or with python directly +python3 -m json.tool charts/opencost/values.schema.json > /dev/null +``` + +## CI/CD Integration + +This chart uses the standard [chart-testing](https://github.com/helm/chart-testing) workflow which automatically: +- Discovers all `ci/*.yaml` files +- Lints each configuration +- Validates against the schema +- Installs and tests in a kind cluster + +The CI workflow is defined in `.github/workflows/helm-test.yml` and uses: +- `.github/configs/ct.yaml` - chart-testing configuration +- `.github/configs/lintconf.yaml` - yamllint configuration + +### How chart-testing Works + +When you add a new file to `ci/`, chart-testing will automatically: +1. **Lint** the chart with those values +2. **Validate** against `values.schema.json` (if `validate-chart-schema: true`) +3. **Install** the chart in a test cluster +4. **Test** the deployment + +No additional workflow changes needed! + +## Adding New Test Scenarios + +When adding new features to the chart: + +1. Create a new `feature-name-values.yaml` file in this directory +2. Include only the values relevant to testing that feature +3. Add documentation to this README +4. Ensure the file passes `helm lint --strict` +5. Test with `helm template` to verify rendered manifests + +## Best Practices + +- **Keep files focused:** Each file should test a specific scenario +- **Use realistic values:** Even test values should be plausible +- **Document purpose:** Clear comments in each file +- **Test combinations:** Consider creating files that test feature interactions +- **Validate schema:** Ensure all values conform to `values.schema.json` + +## Troubleshooting + +### Lint Failures + +If `helm lint` fails: +1. Check the error message for the specific issue +2. Verify values against `values.schema.json` +3. Ensure all required fields are present +4. Check for YAML syntax errors + +### Template Failures + +If `helm template` fails: +1. Check template logic in `templates/` directory +2. Verify conditional statements handle the test values +3. Ensure all referenced values exist +4. Check for missing required values + +## Related Files + +- [`values.yaml`](../values.yaml) - Default chart values +- [`values.schema.json`](../values.schema.json) - JSON Schema for validation +- [`values-openshift.yaml`](../values-openshift.yaml) - OpenShift-specific values \ No newline at end of file diff --git a/charts/opencost/ci/aws-amp-values.yaml b/charts/opencost/ci/aws-amp-values.yaml new file mode 100644 index 00000000..45bf1d3f --- /dev/null +++ b/charts/opencost/ci/aws-amp-values.yaml @@ -0,0 +1,30 @@ +# CI test values - AWS AMP (Amazon Managed Prometheus) +# Tests AWS-specific configuration with AMP and sigV4 proxy +opencost: + prometheus: + amp: + enabled: true + workspaceId: "ws-12345678-1234-1234-1234-123456789012" + internal: + enabled: false + external: + enabled: false + + sigV4Proxy: + region: us-west-2 + host: aps-workspaces.us-west-2.amazonaws.com + port: 8005 + resources: + requests: + cpu: 20m + memory: 32Mi + limits: + cpu: 200m + memory: 500Mi + + exporter: + defaultClusterId: "aws-amp-cluster" + +serviceAccount: + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/opencost-role diff --git a/charts/opencost/ci/cloud-costs-enabled-values.yaml b/charts/opencost/ci/cloud-costs-enabled-values.yaml new file mode 100644 index 00000000..f95d5695 --- /dev/null +++ b/charts/opencost/ci/cloud-costs-enabled-values.yaml @@ -0,0 +1,18 @@ +# CI test values - Cloud Costs Enabled +# Tests cloud cost integration features + +opencost: + cloudCost: + enabled: true + refreshRateHours: 6 + runWindowDays: 3 + monthToDateInterval: 6 + queryWindowDays: 7 + + cloudIntegrationSecret: "cloud-integration-secret" + + exporter: + defaultClusterId: "cloud-costs-cluster" + aws: + access_key_id: "AKIAIOSFODNN7EXAMPLE" + secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" diff --git a/charts/opencost/ci/custom-pricing-values.yaml b/charts/opencost/ci/custom-pricing-values.yaml new file mode 100644 index 00000000..36450771 --- /dev/null +++ b/charts/opencost/ci/custom-pricing-values.yaml @@ -0,0 +1,22 @@ +# CI test values - Custom Pricing +# Tests custom pricing configuration for on-prem deployments + +opencost: + customPricing: + enabled: true + createConfigmap: true + provider: custom + costModel: + description: "Custom on-prem pricing" + CPU: 1.50 + spotCPU: 0.008 + RAM: 0.60 + spotRAM: 0.001 + GPU: 1.20 + storage: 0.30 + zoneNetworkEgress: 0.02 + regionNetworkEgress: 0.02 + internetNetworkEgress: 0.15 + + exporter: + defaultClusterId: "custom-pricing-cluster" diff --git a/charts/opencost/ci/external-prometheus-values.yaml b/charts/opencost/ci/external-prometheus-values.yaml new file mode 100644 index 00000000..4c3d7ef0 --- /dev/null +++ b/charts/opencost/ci/external-prometheus-values.yaml @@ -0,0 +1,16 @@ +# CI test values - External Prometheus +# Tests configuration with external Prometheus (e.g., Grafana Cloud) + +opencost: + prometheus: + external: + enabled: true + url: "https://prometheus.example.com/prometheus" + internal: + enabled: false + username: "test-user" + password: "test-password" + insecureSkipVerify: false + + exporter: + defaultClusterId: "external-prom-cluster" diff --git a/charts/opencost/ci/high-availability-values.yaml b/charts/opencost/ci/high-availability-values.yaml new file mode 100644 index 00000000..2180b722 --- /dev/null +++ b/charts/opencost/ci/high-availability-values.yaml @@ -0,0 +1,40 @@ +# CI test values - High Availability +# Tests HA configuration with multiple replicas, PDB, and resource limits + +opencost: + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/name: opencost + + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: opencost + topologyKey: kubernetes.io/hostname + exporter: + replicas: 3 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 1Gi + +pdb: + enabled: true + minAvailable: 2 + +updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 diff --git a/charts/opencost/ci/ingress-enabled-values.yaml b/charts/opencost/ci/ingress-enabled-values.yaml new file mode 100644 index 00000000..9b8cf434 --- /dev/null +++ b/charts/opencost/ci/ingress-enabled-values.yaml @@ -0,0 +1,36 @@ +# CI test values - Ingress Enabled +# Tests ingress configuration for both UI and API + +opencost: + ui: + enabled: true + ingress: + enabled: true + ingressClassName: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + hosts: + - host: opencost.example.com + paths: + - / + tls: + - secretName: opencost-tls + hosts: + - opencost.example.com + + exporter: + apiIngress: + enabled: true + ingressClassName: "nginx" + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + hosts: + - host: opencost-api.example.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: opencost-api-tls + hosts: + - opencost-api.example.com diff --git a/charts/opencost/ci/mcp-enabled-values.yaml b/charts/opencost/ci/mcp-enabled-values.yaml new file mode 100644 index 00000000..1fc98378 --- /dev/null +++ b/charts/opencost/ci/mcp-enabled-values.yaml @@ -0,0 +1,18 @@ +# CI test values - MCP Server Enabled +# Tests the new MCP (Model Context Protocol) server configuration + +opencost: + mcp: + enabled: true + port: 8081 + ingress: + enabled: true + ingressClassName: "nginx" + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + hosts: + - host: opencost-mcp.example.local + paths: + - path: / + pathType: Prefix + tls: [] diff --git a/charts/opencost/ci/persistence-enabled-values.yaml b/charts/opencost/ci/persistence-enabled-values.yaml new file mode 100644 index 00000000..ac3be6a6 --- /dev/null +++ b/charts/opencost/ci/persistence-enabled-values.yaml @@ -0,0 +1,22 @@ +# CI test values - Persistence Enabled +# Tests persistent volume configuration with collector data source + +opencost: + exporter: + defaultClusterId: "persistence-cluster" + persistence: + enabled: true + mountPath: /mnt/opencost-data + annotations: + volume.beta.kubernetes.io/storage-class: "fast-ssd" + accessMode: ReadWriteOnce + storageClass: "fast-ssd" + size: 10Gi + + collectorDataSource: + enabled: true + scrapeInterval: 30s + networkPort: 3001 + retention10m: 36 + retention1h: 49 + retention1d: 15 diff --git a/charts/opencost/ci/plugins-enabled-values.yaml b/charts/opencost/ci/plugins-enabled-values.yaml new file mode 100644 index 00000000..17ec9400 --- /dev/null +++ b/charts/opencost/ci/plugins-enabled-values.yaml @@ -0,0 +1,30 @@ +# CI test values - Plugins Enabled +# Tests plugin installation and configuration + +plugins: + enabled: true + install: + enabled: true + fullImageName: curlimages/curl:latest + securityContext: + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + folder: /opt/opencost/plugin + configs: + datadog: | + { + "datadog_site": "datadoghq.com", + "datadog_api_key": "test-api-key", + "datadog_app_key": "test-app-key" + } + +opencost: + exporter: + defaultClusterId: "plugins-cluster" diff --git a/charts/opencost/ci/servicemonitor-enabled-values.yaml b/charts/opencost/ci/servicemonitor-enabled-values.yaml new file mode 100644 index 00000000..7456d3ca --- /dev/null +++ b/charts/opencost/ci/servicemonitor-enabled-values.yaml @@ -0,0 +1,35 @@ +# CI test values - ServiceMonitor Enabled +# Tests Prometheus Operator ServiceMonitor configuration + +opencost: + metrics: + serviceMonitor: + enabled: true + additionalLabels: + prometheus: kube-prometheus + release: prometheus-operator + namespace: monitoring + scrapeInterval: 30s + scrapeTimeout: 10s + honorLabels: true + relabelings: + - sourceLabels: [__meta_kubernetes_pod_node_name] + targetLabel: node + metricRelabelings: + - sourceLabels: [__name__] + regex: 'opencost_.*' + action: keep + scheme: http + tlsConfig: {} + + kubeStateMetrics: + emitPodAnnotations: true + emitNamespaceAnnotations: true + emitKsmV1Metrics: false + emitKsmV1MetricsOnly: false + + config: + enabled: true + configmapName: opencost-metrics-config + disabledMetrics: + - opencost_build_info diff --git a/charts/opencost/values.schema.json b/charts/opencost/values.schema.json new file mode 100644 index 00000000..41815c77 --- /dev/null +++ b/charts/opencost/values.schema.json @@ -0,0 +1,1516 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "OpenCost Helm Chart Values Schema", + "type": "object", + "properties": { + "nameOverride": { + "type": "string" + }, + "fullnameOverride": { + "type": "string" + }, + "namespaceOverride": { + "type": "string" + }, + "clusterName": { + "type": "string" + }, + "loglevel": { + "type": "string" + }, + "plugins": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "install": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "fullImageName": { + "type": "string" + }, + "securityContext": { + "type": "object", + "properties": { + "allowPrivilegeEscalation": { + "type": "boolean" + }, + "seccompProfile": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [] + }, + "capabilities": { + "type": "object", + "properties": { + "drop": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [] + }, + "readOnlyRootFilesystem": { + "type": "boolean" + }, + "runAsNonRoot": { + "type": "boolean" + }, + "runAsUser": { + "type": "number" + } + }, + "required": [] + } + }, + "required": [] + }, + "folder": { + "type": "string" + }, + "configs": {} + }, + "required": [] + }, + "imagePullSecrets": { + "type": "array", + "items": {} + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "name": { + "type": "string" + }, + "automountServiceAccountToken": { + "type": "boolean" + } + }, + "required": [] + }, + "networkPolicies": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "prometheus": { + "type": "object", + "properties": { + "namespace": { + "type": "string" + }, + "port": { + "type": "number" + }, + "labels": { + "type": "object", + "properties": { + "app.kubernetes.io/name": { + "type": "string" + } + }, + "required": [] + } + }, + "required": [] + }, + "extraEgress": { + "type": "array", + "items": {} + } + }, + "required": [] + }, + "updateStrategy": { + "type": "object", + "properties": { + "rollingUpdate": { + "type": "object", + "properties": { + "maxSurge": { + "type": "number" + }, + "maxUnavailable": { + "type": "number" + } + }, + "required": [] + }, + "type": { + "type": "string" + } + }, + "required": [] + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "podAnnotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "secretAnnotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "podLabels": { + "type": "object", + "properties": {}, + "required": [] + }, + "priorityClassName": { + "type": ["string", "null"] + }, + "podAutomountServiceAccountToken": { + "type": "boolean" + }, + "podSecurityContext": { + "type": "object", + "properties": {}, + "required": [] + }, + "service": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "labels": { + "type": "object", + "properties": {}, + "required": [] + }, + "type": { + "type": "string" + }, + "nodePort": { + "type": "object", + "properties": {}, + "required": [] + }, + "extraPorts": { + "type": "array", + "items": {} + }, + "loadBalancerSourceRanges": { + "type": "array", + "items": {} + } + }, + "required": [] + }, + "rbac": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [] + }, + "pdb": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "minAvailable": {}, + "maxUnavailable": {} + }, + "required": [] + }, + "opencost": { + "type": "object", + "properties": { + "cloudIntegrationSecret": { + "type": "string" + }, + "mcp": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "port": { + "type": "number" + }, + "ingress": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "ingressClassName": { + "type": "string" + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "paths": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "pathType": { + "type": "string" + } + }, + "required": [] + } + } + }, + "required": [] + } + }, + "tls": { + "type": "array", + "items": {} + } + }, + "required": [] + }, + "httpRoute": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "parentRefs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "sectionName": { + "type": "string" + } + }, + "required": [] + } + }, + "hostnames": { + "type": "array", + "items": { + "type": "string" + } + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "labels": { + "type": "object", + "properties": {}, + "required": [] + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "matches": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [] + } + }, + "required": [] + } + }, + "backendRefs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "port": { + "type": "number" + } + }, + "required": [] + } + } + }, + "required": [] + } + } + }, + "required": [] + } + }, + "required": [] + }, + "exporter": { + "type": "object", + "properties": { + "apiPort": { + "type": "number" + }, + "cloudProviderApiKey": { + "type": "string" + }, + "defaultClusterId": { + "type": "string" + }, + "clusterIdConfigmap": { + "type": "string" + }, + "image": { + "type": "object", + "properties": { + "registry": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "pullPolicy": { + "type": "string" + }, + "fullImageName": { + "type": ["string", "null"] + } + }, + "required": [] + }, + "extraArgs": { + "type": "array", + "items": {} + }, + "command": { + "type": "array", + "items": {} + }, + "replicas": { + "type": "number" + }, + "resources": { + "type": "object", + "properties": { + "requests": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + }, + "required": [] + }, + "limits": { + "type": "object", + "properties": { + "memory": { + "type": "string" + } + }, + "required": [] + } + }, + "required": [] + }, + "startupProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "path": { + "type": "string" + }, + "initialDelaySeconds": { + "type": "number" + }, + "periodSeconds": { + "type": "number" + }, + "failureThreshold": { + "type": "number" + } + }, + "required": [] + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "path": { + "type": "string" + }, + "initialDelaySeconds": { + "type": "number" + }, + "periodSeconds": { + "type": "number" + }, + "failureThreshold": { + "type": "number" + } + }, + "required": [] + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "path": { + "type": "string" + }, + "initialDelaySeconds": { + "type": "number" + }, + "periodSeconds": { + "type": "number" + }, + "failureThreshold": { + "type": "number" + } + }, + "required": [] + }, + "securityContext": { + "type": "object", + "properties": {}, + "required": [] + }, + "csv_path": { + "type": "string" + }, + "prometheusDataSource": { + "type": "object", + "properties": { + "queryResolutionSeconds": { + "type": "number" + } + }, + "required": [] + }, + "collectorDataSource": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "scrapeInterval": { + "type": "string" + }, + "networkPort": { + "type": "number" + }, + "retention10m": { + "type": "number" + }, + "retention1h": { + "type": "number" + }, + "retention1d": { + "type": "number" + } + }, + "required": [] + }, + "persistence": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "mountPath": { + "type": "string" + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "accessMode": { + "type": "string" + }, + "storageClass": { + "type": "string" + }, + "size": { + "type": "string" + } + }, + "required": [] + }, + "aws": { + "type": "object", + "properties": { + "secret_access_key": { + "type": "string" + }, + "access_key_id": { + "type": "string" + } + }, + "required": [] + }, + "extraVolumeMounts": { + "type": "array", + "items": {} + }, + "env": { + "type": "array", + "items": {} + }, + "extraEnv": { + "type": "object", + "properties": {}, + "required": [] + }, + "apiIngress": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "ingressClassName": { + "type": "string" + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "paths": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "pathType": { + "type": "string" + } + }, + "required": [] + } + } + }, + "required": [] + } + }, + "servicePort": { + "type": "string" + }, + "tls": { + "type": "array", + "items": {} + } + }, + "required": [] + }, + "apiHttpRoute": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "parentRefs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "sectionName": { + "type": "string" + } + }, + "required": [] + } + }, + "hostnames": { + "type": "array", + "items": { + "type": "string" + } + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "labels": { + "type": "object", + "properties": {}, + "required": [] + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "matches": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [] + } + }, + "required": [] + } + }, + "backendRefs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "port": { + "type": "number" + } + }, + "required": [] + } + } + }, + "required": [] + } + } + }, + "required": [] + } + }, + "required": [] + }, + "customPricing": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "configmapName": { + "type": "string" + }, + "configPath": { + "type": "string" + }, + "createConfigmap": { + "type": "boolean" + }, + "provider": { + "type": "string" + }, + "costModel": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "CPU": { + "type": "number" + }, + "spotCPU": { + "type": "number" + }, + "RAM": { + "type": "number" + }, + "spotRAM": { + "type": "number" + }, + "GPU": { + "type": "number" + }, + "storage": { + "type": "number" + }, + "zoneNetworkEgress": { + "type": "number" + }, + "regionNetworkEgress": { + "type": "number" + }, + "internetNetworkEgress": { + "type": "number" + } + }, + "required": [] + } + }, + "required": [] + }, + "retention1d": { + "type": "number" + }, + "retention1h": { + "type": "number" + }, + "carbonCost": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [] + }, + "cloudCost": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "refreshRateHours": { + "type": "number" + }, + "runWindowDays": { + "type": "number" + }, + "monthToDateInterval": { + "type": "number" + }, + "queryWindowDays": { + "type": "number" + } + }, + "required": [] + }, + "metrics": { + "type": "object", + "properties": { + "kubeStateMetrics": { + "type": "object", + "properties": { + "emitPodAnnotations": {}, + "emitNamespaceAnnotations": {}, + "emitKsmV1Metrics": {}, + "emitKsmV1MetricsOnly": {} + }, + "required": [] + }, + "serviceMonitor": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "additionalLabels": { + "type": "object", + "properties": {}, + "required": [] + }, + "namespace": { + "type": "string" + }, + "scrapeInterval": { + "type": "string" + }, + "scrapeTimeout": { + "type": "string" + }, + "honorLabels": { + "type": "boolean" + }, + "relabelings": { + "type": "array", + "items": {} + }, + "metricRelabelings": { + "type": "array", + "items": {} + }, + "extraEndpoints": { + "type": "array", + "items": {} + }, + "scheme": { + "type": "string" + }, + "tlsConfig": { + "type": "object", + "properties": {}, + "required": [] + } + }, + "required": [] + }, + "config": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "configmapName": { + "type": "string" + }, + "disabledMetrics": { + "type": "array", + "items": {} + } + }, + "required": [] + } + }, + "required": [] + }, + "prometheus": { + "type": "object", + "properties": { + "secret_name": {}, + "existingSecretName": {}, + "username": { + "type": "string" + }, + "username_key": { + "type": "string" + }, + "password": { + "type": "string" + }, + "password_key": { + "type": "string" + }, + "bearer_token": { + "type": "string" + }, + "bearer_token_key": { + "type": "string" + }, + "kubeRBACProxy": { + "type": "boolean" + }, + "insecureSkipVerify": { + "type": "boolean" + }, + "external": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "url": { + "type": "string" + } + }, + "required": [] + }, + "internal": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "serviceName": { + "type": "string" + }, + "namespaceName": { + "type": "string" + }, + "port": { + "type": "number" + }, + "path": { + "type": "string" + }, + "scheme": { + "type": "string" + } + }, + "required": [] + }, + "amp": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "workspaceId": { + "type": "string" + } + }, + "required": [] + }, + "thanos": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "queryOffset": { + "type": "string" + }, + "maxSourceResolution": { + "type": "string" + }, + "internal": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "serviceName": { + "type": "string" + }, + "namespaceName": { + "type": "string" + }, + "port": { + "type": "number" + }, + "scheme": { + "type": "string" + } + }, + "required": [] + }, + "external": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "url": { + "type": "string" + } + }, + "required": [] + } + }, + "required": [] + } + }, + "required": [] + }, + "ui": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "image": { + "type": "object", + "properties": { + "registry": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "pullPolicy": { + "type": "string" + }, + "fullImageName": {} + }, + "required": [] + }, + "resources": { + "type": "object", + "properties": { + "requests": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + }, + "required": [] + }, + "limits": { + "type": "object", + "properties": { + "memory": { + "type": "string" + } + }, + "required": [] + } + }, + "required": [] + }, + "uiPort": { + "type": "number" + }, + "uiPath": { + "type": "string" + }, + "useIPv6": { + "type": "boolean" + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "path": { + "type": "string" + }, + "initialDelaySeconds": { + "type": "number" + }, + "periodSeconds": { + "type": "number" + }, + "failureThreshold": { + "type": "number" + } + }, + "required": [] + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "path": { + "type": "string" + }, + "initialDelaySeconds": { + "type": "number" + }, + "periodSeconds": { + "type": "number" + }, + "failureThreshold": { + "type": "number" + } + }, + "required": [] + }, + "securityContext": { + "type": "object", + "properties": {}, + "required": [] + }, + "extraEnv": { + "type": "array", + "items": {} + }, + "extraVolumeMounts": { + "type": "array", + "items": {} + }, + "useDefaultFqdn": { + "type": "boolean" + }, + "nginx": { + "type": "object", + "properties": { + "proxyConnectTimeout": { + "type": "number" + }, + "proxySendTimeout": { + "type": "number" + }, + "proxyReadTimeout": { + "type": "number" + } + }, + "required": [] + }, + "ingress": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "ingressClassName": { + "type": "string" + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "type": "string" + }, + "paths": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [] + } + }, + "servicePort": { + "type": "string" + }, + "tls": { + "type": "array", + "items": {} + } + }, + "required": [] + }, + "route": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "host": { + "type": "string" + }, + "path": {}, + "targetPort": { + "type": "string" + }, + "tls": { + "type": "object" + } + }, + "required": [] + }, + "httpRoute": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "parentRefs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "sectionName": { + "type": "string" + } + }, + "required": [] + } + }, + "hostnames": { + "type": "array", + "items": { + "type": "string" + } + }, + "annotations": { + "type": "object", + "properties": {}, + "required": [] + }, + "labels": { + "type": "object", + "properties": {}, + "required": [] + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "matches": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [] + } + }, + "required": [] + } + }, + "backendRefs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "port": { + "type": "number" + } + }, + "required": [] + } + } + }, + "required": [] + } + } + }, + "required": [] + } + }, + "required": [] + }, + "sigV4Proxy": { + "type": "object", + "properties": { + "image": { + "type": "string" + }, + "imagePullPolicy": { + "type": "string" + }, + "name": { + "type": "string" + }, + "port": { + "type": "number" + }, + "region": { + "type": "string" + }, + "host": { + "type": "string" + }, + "extraEnv": {}, + "resources": { + "type": "object", + "properties": {}, + "required": [] + }, + "securityContext": { + "type": "object", + "properties": {}, + "required": [] + } + }, + "required": [] + }, + "tolerations": { + "type": "array", + "items": {} + }, + "nodeSelector": { + "type": "object", + "properties": {}, + "required": [] + }, + "affinity": { + "type": "object", + "properties": {}, + "required": [] + }, + "topologySpreadConstraints": { + "type": "array", + "items": {} + }, + "extraContainers": { + "type": "array", + "items": {} + }, + "updateCaTrust": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "securityContext": { + "type": "object", + "properties": { + "runAsUser": { + "type": "number" + }, + "runAsGroup": { + "type": "number" + }, + "runAsNonRoot": { + "type": "boolean" + }, + "allowPrivilegeEscalation": { + "type": "boolean" + }, + "readOnlyRootFilesystem": { + "type": "boolean" + }, + "seccompProfile": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [] + } + }, + "required": [] + }, + "caCertsSecret": { + "type": "string" + }, + "resources": { + "type": "object", + "properties": {}, + "required": [] + } + }, + "required": [] + }, + "platforms": { + "type": "object", + "properties": { + "openshift": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "createMonitoringClusterRoleBinding": { + "type": "boolean" + }, + "createMonitoringResourceReaderRoleBinding": { + "type": "boolean" + }, + "monitoringServiceAccountName": { + "type": "string" + }, + "monitoringServiceAccountNamespace": { + "type": "string" + }, + "enableSCC": { + "type": "boolean" + }, + "enablePromAccess": { + "type": "boolean" + } + }, + "required": [] + } + }, + "required": [] + } + }, + "required": [] + }, + "extraVolumes": { + "type": "array", + "items": {} + }, + "extraObjects": { + "type": "array", + "items": {} + } + }, + "required": [], + "additionalProperties": false +} \ No newline at end of file