diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 788e057..8260cfd 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -11,18 +11,14 @@ permissions: jobs: kind-helm: - strategy: - matrix: - helm-version: - - v3.11.0 runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Kubernetes - uses: helm/kind-action@v1.5.0 + uses: helm/kind-action@v1.8.0 with: - version: v0.17.0 + version: v0.20.0 cluster_name: kind - name: Build container image run: | @@ -31,7 +27,7 @@ jobs: - name: Setup Helm uses: azure/setup-helm@v3 with: - version: ${{ matrix.helm-version }} + version: v3.12.3 - name: Deploy run: ./test/deploy.sh - name: Run integration tests @@ -40,3 +36,42 @@ jobs: if: failure() run: | kubectl logs -l app=podinfo || true + kind-timoni: + runs-on: ubuntu-latest + services: + registry: + image: registry:2 + ports: + - 5000:5000 + env: + PODINFO_IMAGE_URL: "test/podinfo" + PODINFO_MODULE_URL: "oci://localhost:5000/podinfo" + PODINFO_VERSION: "0.0.0-devel" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Timoni + uses: stefanprodan/timoni/actions/setup@main + - name: Setup Kubernetes + uses: helm/kind-action@v1.8.0 + with: + version: v0.20.0 + cluster_name: kind + - name: Build container + run: | + docker build -t ${PODINFO_IMAGE_URL}:${PODINFO_VERSION} --build-arg "REVISION=${GITHUB_SHA}" -f Dockerfile.xx . + kind load docker-image ${PODINFO_IMAGE_URL}:${PODINFO_VERSION} + - name: Build module + run: | + timoni mod push ./timoni/podinfo ${PODINFO_MODULE_URL} -v ${PODINFO_VERSION} + - name: Apply bundle + run: | + timoni bundle apply -f ./timoni/bundles/test.podinfo.cue --runtime-from-env + - name: Verify status + run: | + timoni -n podinfo status backend + timoni -n podinfo status frontend + - name: Debug failure + if: failure() + run: | + kubectl -n podinfo get all || true diff --git a/timoni/bundles/test.podinfo.cue b/timoni/bundles/test.podinfo.cue new file mode 100644 index 0000000..1d4fda9 --- /dev/null +++ b/timoni/bundles/test.podinfo.cue @@ -0,0 +1,66 @@ +bundle: { + apiVersion: "v1alpha1" + name: "podinfo" + + _modURL: "oci://ghcr.io/stefanprodan/modules/podinfo" @timoni(runtime:string:PODINFO_MODULE_URL) + _imgURL: "ghcr.io/stefanprodan/modules/podinfo" @timoni(runtime:string:PODINFO_IMAGE_URL) + _imgTag: "latest" @timoni(runtime:string:PODINFO_VERSION) + + instances: { + backend: { + module: url: _modURL + namespace: "podinfo" + values: { + image: { + repository: _imgURL + tag: _imgTag + } + resources: requests: { + cpu: "100m" + memory: "128Mi" + } + autoscaling: { + enabled: true + minReplicas: 1 + maxReplicas: 10 + cpu: 90 + } + } + } + frontend: { + module: url: _modURL + namespace: "podinfo" + values: { + image: { + repository: _imgURL + tag: _imgTag + } + ui: backend: "http://backend.podinfo.svc.cluster.local/echo" + replicas: 2 + podSecurityContext: { + runAsUser: 100 + runAsGroup: 101 + fsGroup: 101 + } + securityContext: { + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: drop: ["ALL"] + seccompProfile: type: "RuntimeDefault" + } + ingress: { + enabled: true + className: "nginx" + host: "podinfo.local" + tls: true + annotations: { + "nginx.ingress.kubernetes.io/ssl-redirect": "false" + "nginx.ingress.kubernetes.io/force-ssl-redirect": "false" + "cert-manager.io/cluster-issuer": "self-signed" + } + } + } + } + } +} diff --git a/timoni/podinfo/README.md b/timoni/podinfo/README.md index e923f15..92294ba 100644 --- a/timoni/podinfo/README.md +++ b/timoni/podinfo/README.md @@ -19,7 +19,7 @@ timoni -n default apply podinfo oci://ghcr.io/stefanprodan/modules/podinfo To install a specific module version: ```shell -timoni -n default apply podinfo oci://ghcr.io/stefanprodan/modules/podinfo -v 6.3.5 +timoni -n default apply podinfo oci://ghcr.io/stefanprodan/modules/podinfo -v 6.5.0 ``` To change the [default configuration](#configuration), @@ -118,10 +118,10 @@ values: { ### Monitoring values -| Key | Type | Default | Description | -|-------------------------|----------|---------|-------------------------------------------------------------------------------| -| `monitoring: enabled:` | `bool` | `false` | Enable [Prometheus ServiceMonitor](https://prometheus-operator.dev/) creation | -| `monitoring: interval:` | `string` | `15s` | Prometheus scrape interval | +| Key | Type | Default | Description | +|-------------------------|--------|---------|-------------------------------------------------------------------------------| +| `monitoring: enabled:` | `bool` | `false` | Enable [Prometheus ServiceMonitor](https://prometheus-operator.dev/) creation | +| `monitoring: interval:` | `int` | `15` | Prometheus scrape interval in seconds | ### Cashing values @@ -129,3 +129,11 @@ values: { |----------------------|----------|---------|---------------------------------------------------------| | `caching: enabled:` | `bool` | `false` | Enable Redis caching | | `caching: redisURL:` | `string` | `""` | Redis URL in the format `tcp://:[password]@host[:port]` | + +### UI values + +| Key | Type | Default | Description | +|----------------|----------|-----------|------------------| +| `ui: color:` | `string` | `#34577c` | Background color | +| `ui: message:` | `string` | `""` | Greeting message | +| `ui: backend:` | `string` | `""` | Backend URL | diff --git a/timoni/podinfo/templates/config.cue b/timoni/podinfo/templates/config.cue index 5fb40ef..1260a9f 100644 --- a/timoni/podinfo/templates/config.cue +++ b/timoni/podinfo/templates/config.cue @@ -7,6 +7,13 @@ import ( // Config defines the schema and defaults for the Instance values. #Config: { + // UI setting + ui: { + color: *"#34577c" | string + message?: string + backend?: string + } + // Runtime version info moduleVersion!: string kubeVersion!: string @@ -16,7 +23,7 @@ import ( metadata: version: moduleVersion // Deployment - replicas: *1 | int & >0 + replicas: *1 | int & >=0 // Pod podAnnotations?: {[ string]: string} @@ -33,7 +40,11 @@ import ( securityContext?: corev1.#SecurityContext // Service - service: port: *80 | int & >0 & <=65535 + service: { + port: *80 | int & >0 & <=65535 + annotations?: {[ string]: string} + labels?: {[ string]: string} + } // HorizontalPodAutoscaler (optional) autoscaling: { @@ -50,13 +61,14 @@ import ( tls: *false | bool host: *"podinfo.local" | string annotations?: {[ string]: string} + labels?: {[ string]: string} className?: string } // ServiceMonitor (optional) monitoring: { enabled: *false | bool - interval: *"15s" | string + interval: *15 | int & >=5 & <=3600 } // Caching (optional) diff --git a/timoni/podinfo/templates/deployment.cue b/timoni/podinfo/templates/deployment.cue index 5bb932a..00b56ac 100644 --- a/timoni/podinfo/templates/deployment.cue +++ b/timoni/podinfo/templates/deployment.cue @@ -21,6 +21,10 @@ import ( if !_config.autoscaling.enabled { replicas: _config.replicas } + strategy: { + type: "RollingUpdate" + rollingUpdate: maxUnavailable: "50%" + } selector: matchLabels: _config.metadata.labelSelector template: { metadata: { @@ -28,6 +32,12 @@ import ( if _config.podAnnotations != _|_ { annotations: _config.podAnnotations } + if !_config.monitoring.enabled { + annotations: { + "prometheus.io/scrape": "true" + "prometheus.io/port": "9797" + } + } } spec: corev1.#PodSpec & { serviceAccountName: _config.metadata.name @@ -42,6 +52,11 @@ import ( containerPort: 9898 protocol: "TCP" }, + { + name: "http-metrics" + containerPort: 9797 + protocol: "TCP" + }, ] livenessProbe: { httpGet: { @@ -61,13 +76,39 @@ import ( if _config.securityContext != _|_ { securityContext: _config.securityContext } + env: [ + { + name: "PODINFO_UI_COLOR" + value: _config.ui.color + }, + if _config.ui.message != _|_ { + { + name: "PODINFO_UI_MESSAGE" + value: _config.ui.message + } + }, + if _config.ui.backend != _|_ { + { + name: "PODINFO_BACKEND_URL" + value: _config.ui.backend + } + }, + ] command: [ "./podinfo", "--level=info", + "--port=9898", + "--port-metrics=9797", if _config.caching.enabled { "--cache-server=\(_config.caching.redisURL)" }, ] + volumeMounts: [ + { + name: "data" + mountPath: "/data" + }, + ] }, ] if _config.podSecurityContext != _|_ { @@ -85,6 +126,12 @@ import ( if _config.imagePullSecrets != _|_ { imagePullSecrets: _config.imagePullSecrets } + volumes: [ + { + name: "data" + emptyDir: {} + }, + ] } } } diff --git a/timoni/podinfo/templates/ingress.cue b/timoni/podinfo/templates/ingress.cue index 0ff98c4..dac1f8b 100644 --- a/timoni/podinfo/templates/ingress.cue +++ b/timoni/podinfo/templates/ingress.cue @@ -12,6 +12,9 @@ import ( name: _config.metadata.name namespace: _config.metadata.namespace labels: _config.metadata.labels + if _config.ingress.labels != _|_ { + labels: _config.ingress.labels + } if _config.metadata.annotations != _|_ { annotations: _config.metadata.annotations } diff --git a/timoni/podinfo/templates/service.cue b/timoni/podinfo/templates/service.cue index 251bfd9..d286abf 100644 --- a/timoni/podinfo/templates/service.cue +++ b/timoni/podinfo/templates/service.cue @@ -12,9 +12,15 @@ import ( name: _config.metadata.name namespace: _config.metadata.namespace labels: _config.metadata.labels + if _config.service.labels != _|_ { + labels: _config.service.labels + } if _config.metadata.annotations != _|_ { annotations: _config.metadata.annotations } + if _config.service.annotations != _|_ { + annotations: _config.service.annotations + } } spec: corev1.#ServiceSpec & { type: corev1.#ServiceTypeClusterIP @@ -26,6 +32,14 @@ import ( targetPort: "\(name)" protocol: "TCP" }, + if _config.monitoring.enabled { + { + name: "http-metrics" + port: 9797 + targetPort: "http-metrics" + protocol: "TCP" + } + }, ] } } diff --git a/timoni/podinfo/templates/servicemonitor.cue b/timoni/podinfo/templates/servicemonitor.cue index 76913f5..8d3a329 100644 --- a/timoni/podinfo/templates/servicemonitor.cue +++ b/timoni/podinfo/templates/servicemonitor.cue @@ -18,7 +18,7 @@ import ( endpoints: [{ path: "/metrics" port: "http-metrics" - interval: _config.monitoring.interval + interval: "\(_config.monitoring.interval)s" }] namespaceSelector: matchNames: [_config.metadata.namespace] selector: matchLabels: _config.metadata.labelSelector diff --git a/timoni/podinfo/test_values.cue b/timoni/podinfo/test_values.cue index f5f8288..5a8f239 100644 --- a/timoni/podinfo/test_values.cue +++ b/timoni/podinfo/test_values.cue @@ -3,6 +3,8 @@ package main values: { + ui: backend: "http://backend.default.svc.cluster.local/echo" + metadata: { labels: "app.kubernetes.io/part-of": "podinfo" annotations: "app.kubernetes.io/team": "dev" @@ -21,7 +23,7 @@ values: { annotations: "cert-manager.io/cluster-issuer": "letsencrypt" } - monitoring: enabled: true + monitoring: enabled: false _mcpu: 100 _mem: 128