From 01eb690ffbd74129cffae1660e36c8ac2db69cac Mon Sep 17 00:00:00 2001 From: Andreas Grub Date: Fri, 27 Feb 2026 09:25:07 +0100 Subject: [PATCH 1/2] chore: add plan for SKE starter kit --- ske-starterkit-refactoring-plan.md | 89 ++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 ske-starterkit-refactoring-plan.md diff --git a/ske-starterkit-refactoring-plan.md b/ske-starterkit-refactoring-plan.md new file mode 100644 index 0000000..85c8790 --- /dev/null +++ b/ske-starterkit-refactoring-plan.md @@ -0,0 +1,89 @@ +# Plan: Adapt SKE Starterkit to Match AKS Composition Pattern + +## Objective + +1. **Create a git worktree** at `.worktrees/aks-refactor` for `feature/aks-starter-kit-refactoring` as a reference +2. **Refactor the SKE starterkit** to follow the AKS composition pattern (backplane + child BBDs), using STACKIT components instead of Azure/GitHub equivalents + +## Current State Analysis + +### AKS Starterkit Pattern (the target pattern) +The refactored AKS starterkit uses a **composition pattern**: +- **`starterkit/backplane/`** — Terraform module that references child BBD `meshstack_integration.tf` modules: + - `modules/github/repository` → registers the GitHub Repo BBD + - `modules/aks/github-connector` → registers the GitHub Actions Connector BBD + - `modules/azure/postgresql` → (optional) registers the PostgreSQL BBD +- **`starterkit/meshstack_integration.tf`** — Single-file module that: + - Calls `module "backplane" { source = "./backplane" }` to register child BBDs + - Registers the starterkit BBD itself, wiring child BBD UUIDs as static inputs + - Exposes `var.github`, `var.aks`, `var.hub`, `var.meshstack` + +### SKE Starterkit (current — to be refactored) +Currently uses a **flat pattern**: +- **No real backplane** for the starterkit (only the SKE platform itself has `ske/backplane/`) +- **`starterkit/buildingblock/main.tf`** directly creates meshStack projects, tenants, user bindings, and instantiates the Git repo BBD inline via `meshstack_building_block_v2` +- **`starterkit/meshstack_integration.tf`** — registers the starterkit BBD with `var.starterkit.git_repo_definition_uuid` / `_version_uuid` as manual external inputs + +### Existing STACKIT Hub Modules +- **`modules/stackit/git-repository/`** — Already exists with full backplane + buildingblock + `meshstack_integration.tf`. Uses Gitea provider for Forgejo. ✅ Ready to reuse. +- **No Forgejo Actions connector module exists yet** — This needs to be created (analogous to `modules/aks/github-connector/`). + +### STACKIT Forgejo Actions Connector (to be created) +Looking at the AKS GitHub Actions connector (`modules/aks/github-connector/`), it: +1. Creates a Kubernetes service account + RBAC in the target namespace +2. Generates a kubeconfig for the service account +3. Stores the kubeconfig as a GitHub Actions environment secret +4. Stores container registry credentials as environment secrets + +The STACKIT equivalent would: +1. Create a Kubernetes service account + RBAC in the SKE namespace (same pattern) +2. Generate a kubeconfig for the service account +3. Store secrets in the Forgejo repository (using Gitea provider's `gitea_actions_secret` or via API) +4. No ACR — STACKIT uses its own container registry (or none initially) + +**Note**: Forgejo Actions are similar to GitHub Actions (they share the same runner spec). The connector module pattern is analogous — the difference is the secret storage target (Forgejo repo secrets vs GitHub environment secrets). + +## Todos + +### 1. `worktree-setup` — Create AKS refactor worktree +Create `.worktrees/aks-refactor` for `feature/aks-starter-kit-refactoring` as side-by-side reference. + +### 2. `forgejo-connector-module` — Create `modules/stackit/ske/forgejo-connector/` +New hub module analogous to `modules/aks/github-connector/`: +- **`buildingblock/`**: + - `main.tf` — Create K8s service account + RBAC in SKE namespace, generate kubeconfig, store as Forgejo Actions secret + - `variables.tf` — `namespace`, `gitea_base_url`, `gitea_token`, `gitea_organization`, `repository_name` + - `outputs.tf` — summary output + - `provider.tf` — kubernetes + gitea providers + - `versions.tf` — provider version pins + - `README.md` — with YAML front-matter + - `APP_TEAM_README.md` — user-facing docs + - `logo.png` — reuse STACKIT logo +- **`meshstack_integration.tf`** — Single-file module registering the Forgejo connector BBD + +### 3. `ske-starterkit-backplane` — Create `modules/stackit/ske/starterkit/backplane/` +New backplane following AKS pattern: +- `main.tf` — References child BBDs: + - `module "git_repo_bbd"` → sources `../../git-repository` (relative path to `modules/stackit/git-repository`) + - `module "forgejo_connector_bbd"` → sources `../forgejo-connector` (relative path, once created) +- `variables.tf` — `var.hub`, `var.meshstack`, `var.gitea` (Forgejo credentials) +- `outputs.tf` — child BBD UUIDs for wiring +- `versions.tf` — provider pins + +### 4. `ske-starterkit-integration-refactor` — Update `modules/stackit/ske/starterkit/meshstack_integration.tf` +Refactor to match AKS pattern: +- Add `module "backplane" { source = "./backplane" }` call +- Replace `var.starterkit.git_repo_definition_uuid` / `_version_uuid` with `module.backplane.*` outputs +- Add `var.gitea` variable for Forgejo credentials +- Remove manual UUID inputs from `var.starterkit` (they now come from backplane) +- Keep: `var.hub`, `var.meshstack`, `var.aks`→`var.ske` platform identifiers + +### 5. `ske-starterkit-buildingblock-update` — Update buildingblock inputs +- Add Forgejo connector inputs (e.g. `forgejo_connector_definition_version_uuid`) +- Wire the connector BBD into the per-tenant composition (similar to how AKS wires `github_actions_connector_definition_version_uuid`) + +## Open Questions / Decisions + +1. **Forgejo Actions secrets API**: Does the Gitea Terraform provider support `gitea_actions_secret`? If not, we may need `null_resource` + `curl` to the Forgejo API (similar to the existing webhook pattern in `stackit/git-repository/buildingblock/main.tf`). +2. **Container registry**: AKS has ACR credentials pushed as secrets. Does STACKIT have an equivalent container registry, or should this be left out initially? +3. **Scope of this PR**: Should the Forgejo connector module be a separate PR/branch, or part of this SKE starterkit refactoring? From 2be1802b16b367c395fdc38be9f2303808b5a844 Mon Sep 17 00:00:00 2001 From: Andreas Grub Date: Fri, 27 Feb 2026 09:08:12 +0100 Subject: [PATCH 2/2] feat: add SKE starterkit composition with Forgejo connector and Harbor registry - Switch git-repository module provider from Lerentis/gitea to go-gitea/gitea - Add Forgejo Actions connector module (SKE kubeconfig + repo secrets) - Add SKE starterkit composition backplane + integration (follows AKS pattern) - Add optional Harbor container registry credential injection - Stores push credentials as Forgejo Actions repository secrets - Creates dockerconfigjson pull secret in Kubernetes namespace - All modules use relative paths and var.hub.git_ref convention Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../git-repository/buildingblock/versions.tf | 4 +- .../git-repository/meshstack_integration.tf | 10 + modules/stackit/ske/backplane/README.md | 82 ++++++ modules/stackit/ske/backplane/main.tf | 55 ++++ modules/stackit/ske/backplane/outputs.tf | 50 ++++ modules/stackit/ske/backplane/variables.tf | 76 ++++++ modules/stackit/ske/backplane/versions.tf | 14 + .../buildingblock/APP_TEAM_README.md | 41 +++ .../forgejo-connector/buildingblock/README.md | 19 ++ .../forgejo-connector/buildingblock/logo.png | Bin 0 -> 3594 bytes .../forgejo-connector/buildingblock/main.tf | 128 +++++++++ .../buildingblock/outputs.tf | 35 +++ .../buildingblock/provider.tf | 4 + .../buildingblock/variables.tf | 53 ++++ .../buildingblock/versions.tf | 14 + .../meshstack_integration.tf | 183 +++++++++++++ modules/stackit/ske/meshstack_integration.tf | 158 +++++++++++ .../stackit/ske/starterkit/backplane/main.tf | 22 ++ .../ske/starterkit/backplane/outputs.tf | 14 + .../ske/starterkit/backplane/variables.tf | 42 +++ .../ske/starterkit/backplane/versions.tf | 10 + .../buildingblock/APP_TEAM_README.md | 41 +++ .../ske/starterkit/buildingblock/README.md | 18 ++ .../ske/starterkit/buildingblock/logo.png | Bin 0 -> 3594 bytes .../ske/starterkit/buildingblock/main.tf | 162 ++++++++++++ .../ske/starterkit/buildingblock/outputs.tf | 56 ++++ .../ske/starterkit/buildingblock/variables.tf | 86 ++++++ .../ske/starterkit/buildingblock/versions.tf | 14 + .../ske/starterkit/meshstack_integration.tf | 248 ++++++++++++++++++ ske-starterkit-refactoring-plan.md | 15 +- 30 files changed, 1646 insertions(+), 8 deletions(-) create mode 100644 modules/stackit/ske/backplane/README.md create mode 100644 modules/stackit/ske/backplane/main.tf create mode 100644 modules/stackit/ske/backplane/outputs.tf create mode 100644 modules/stackit/ske/backplane/variables.tf create mode 100644 modules/stackit/ske/backplane/versions.tf create mode 100644 modules/stackit/ske/forgejo-connector/buildingblock/APP_TEAM_README.md create mode 100644 modules/stackit/ske/forgejo-connector/buildingblock/README.md create mode 100644 modules/stackit/ske/forgejo-connector/buildingblock/logo.png create mode 100644 modules/stackit/ske/forgejo-connector/buildingblock/main.tf create mode 100644 modules/stackit/ske/forgejo-connector/buildingblock/outputs.tf create mode 100644 modules/stackit/ske/forgejo-connector/buildingblock/provider.tf create mode 100644 modules/stackit/ske/forgejo-connector/buildingblock/variables.tf create mode 100644 modules/stackit/ske/forgejo-connector/buildingblock/versions.tf create mode 100644 modules/stackit/ske/forgejo-connector/meshstack_integration.tf create mode 100644 modules/stackit/ske/meshstack_integration.tf create mode 100644 modules/stackit/ske/starterkit/backplane/main.tf create mode 100644 modules/stackit/ske/starterkit/backplane/outputs.tf create mode 100644 modules/stackit/ske/starterkit/backplane/variables.tf create mode 100644 modules/stackit/ske/starterkit/backplane/versions.tf create mode 100644 modules/stackit/ske/starterkit/buildingblock/APP_TEAM_README.md create mode 100644 modules/stackit/ske/starterkit/buildingblock/README.md create mode 100644 modules/stackit/ske/starterkit/buildingblock/logo.png create mode 100644 modules/stackit/ske/starterkit/buildingblock/main.tf create mode 100644 modules/stackit/ske/starterkit/buildingblock/outputs.tf create mode 100644 modules/stackit/ske/starterkit/buildingblock/variables.tf create mode 100644 modules/stackit/ske/starterkit/buildingblock/versions.tf create mode 100644 modules/stackit/ske/starterkit/meshstack_integration.tf diff --git a/modules/stackit/git-repository/buildingblock/versions.tf b/modules/stackit/git-repository/buildingblock/versions.tf index d26605a..df2b03f 100644 --- a/modules/stackit/git-repository/buildingblock/versions.tf +++ b/modules/stackit/git-repository/buildingblock/versions.tf @@ -3,8 +3,8 @@ terraform { required_providers { gitea = { - source = "Lerentis/gitea" - version = "~> 0.16.0" + source = "go-gitea/gitea" + version = "~> 0.7.0" } null = { source = "hashicorp/null" diff --git a/modules/stackit/git-repository/meshstack_integration.tf b/modules/stackit/git-repository/meshstack_integration.tf index e50438b..584f3c5 100644 --- a/modules/stackit/git-repository/meshstack_integration.tf +++ b/modules/stackit/git-repository/meshstack_integration.tf @@ -182,3 +182,13 @@ resource "meshstack_building_block_definition" "stackit_git_repo" { } } } + +output "bbd_uuid" { + description = "UUID of the STACKIT Git repository building block definition." + value = meshstack_building_block_definition.stackit_git_repo.ref.uuid +} + +output "bbd_version_uuid" { + description = "UUID of the latest version of the STACKIT Git repository building block definition." + value = meshstack_building_block_definition.stackit_git_repo.version_latest.uuid +} diff --git a/modules/stackit/ske/backplane/README.md b/modules/stackit/ske/backplane/README.md new file mode 100644 index 0000000..1e62fc9 --- /dev/null +++ b/modules/stackit/ske/backplane/README.md @@ -0,0 +1,82 @@ +# SKE Backplane + +This module provisions the STACKIT Kubernetes Engine (SKE) cluster and sets up the +meshStack platform integration (replicator and metering service accounts). + +## What it creates + +- **SKE Cluster** with a configurable node pool (machine type, count, availability zones) +- **Kubeconfig** for cluster access (180-day expiration, auto-refresh) +- **meshStack platform integration** via [terraform-kubernetes-meshplatform](https://github.com/meshcloud/terraform-kubernetes-meshplatform): + - Replicator service account for namespace provisioning + - Metering service account for usage data collection + +## Usage + +This module is called from `meshstack_integration.tf` and its outputs are wired into the +`meshstack_platform` resource's `config.kubernetes` block. + +```hcl +module "backplane" { + source = "./backplane" + + stackit_project_id = "your-project-id" + cluster_name = "ske-cluster" + region = "eu01" +} +``` + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [kubernetes](#requirement\_kubernetes) | ~> 2.0 | +| [stackit](#requirement\_stackit) | >= 0.68.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [meshplatform](#module\_meshplatform) | git::https://github.com/meshcloud/terraform-kubernetes-meshplatform.git | v0.1.0 | + +## Resources + +| Name | Type | +|------|------| +| [stackit_ske_cluster.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/ske_cluster) | resource | +| [stackit_ske_kubeconfig.this](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/resources/ske_kubeconfig) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [availability\_zones](#input\_availability\_zones) | Availability zones for the default node pool. | `list(string)` |
[
"eu01-1"
]
| no | +| [cluster\_name](#input\_cluster\_name) | Name of the SKE cluster. | `string` | `"ske-cluster"` | no | +| [enable\_kubernetes\_version\_updates](#input\_enable\_kubernetes\_version\_updates) | Enable automatic Kubernetes version updates during maintenance windows. | `bool` | `true` | no | +| [enable\_machine\_image\_version\_updates](#input\_enable\_machine\_image\_version\_updates) | Enable automatic machine image version updates during maintenance windows. | `bool` | `true` | no | +| [machine\_type](#input\_machine\_type) | Machine type for the default node pool. | `string` | `"c2i.2"` | no | +| [maintenance\_end](#input\_maintenance\_end) | End of the maintenance window (UTC). | `string` | `"06:00:00Z"` | no | +| [maintenance\_start](#input\_maintenance\_start) | Start of the maintenance window (UTC). | `string` | `"02:00:00Z"` | no | +| [meshplatform\_namespace](#input\_meshplatform\_namespace) | Kubernetes namespace for the meshStack platform integration (replicator + metering service accounts). | `string` | `"meshcloud"` | no | +| [node\_count](#input\_node\_count) | Number of nodes in the default node pool. | `number` | `1` | no | +| [region](#input\_region) | STACKIT region for the SKE cluster. | `string` | `"eu01"` | no | +| [stackit\_project\_id](#input\_stackit\_project\_id) | STACKIT project ID where the SKE cluster will be created. | `string` | n/a | yes | +| [volume\_size](#input\_volume\_size) | Volume size in GB for nodes in the default node pool. | `number` | `25` | no | +| [volume\_type](#input\_volume\_type) | Volume type for nodes in the default node pool. | `string` | `"storage_premium_perf0"` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [client\_certificate](#output\_client\_certificate) | PEM-encoded client certificate for authentication. | +| [client\_key](#output\_client\_key) | PEM-encoded client key for authentication. | +| [cluster\_ca\_certificate](#output\_cluster\_ca\_certificate) | PEM-encoded CA certificate for the cluster. | +| [cluster\_name](#output\_cluster\_name) | Name of the SKE cluster. | +| [console\_url](#output\_console\_url) | URL to the STACKIT portal for this SKE cluster. | +| [kube\_host](#output\_kube\_host) | Kubernetes API server endpoint. | +| [kubernetes\_version](#output\_kubernetes\_version) | Kubernetes version running on the cluster. | +| [metering\_token](#output\_metering\_token) | Access token for the meshStack metering service account. | +| [replicator\_token](#output\_replicator\_token) | Access token for the meshStack replicator service account. | + \ No newline at end of file diff --git a/modules/stackit/ske/backplane/main.tf b/modules/stackit/ske/backplane/main.tf new file mode 100644 index 0000000..212dd23 --- /dev/null +++ b/modules/stackit/ske/backplane/main.tf @@ -0,0 +1,55 @@ +resource "stackit_ske_cluster" "this" { + project_id = var.stackit_project_id + name = var.cluster_name + node_pools = [ + { + name = "default" + machine_type = var.machine_type + minimum = var.node_count + maximum = var.node_count + availability_zones = var.availability_zones + volume_size = var.volume_size + volume_type = var.volume_type + } + ] + maintenance = { + enable_kubernetes_version_updates = var.enable_kubernetes_version_updates + enable_machine_image_version_updates = var.enable_machine_image_version_updates + start = var.maintenance_start + end = var.maintenance_end + } + + lifecycle { + ignore_changes = [kubernetes_version_used, node_pools[0].os_version_used] + } +} + +resource "stackit_ske_kubeconfig" "this" { + project_id = var.stackit_project_id + cluster_name = stackit_ske_cluster.this.name + expiration = "15552000" # 180 days + refresh = true +} + +locals { + kubeconfig = yamldecode(stackit_ske_kubeconfig.this.kube_config) + kube_host = local.kubeconfig["clusters"][0]["cluster"]["server"] + cluster_ca_certificate = base64decode(local.kubeconfig["clusters"][0]["cluster"]["certificate-authority-data"]) + client_certificate = base64decode(local.kubeconfig["users"][0]["user"]["client-certificate-data"]) + client_key = base64decode(local.kubeconfig["users"][0]["user"]["client-key-data"]) +} + +provider "kubernetes" { + host = local.kube_host + cluster_ca_certificate = local.cluster_ca_certificate + client_certificate = local.client_certificate + client_key = local.client_key +} + +module "meshplatform" { + source = "git::https://github.com/meshcloud/terraform-kubernetes-meshplatform.git?ref=v0.1.0" + + namespace = var.meshplatform_namespace + replicator_enabled = true + metering_enabled = true +} diff --git a/modules/stackit/ske/backplane/outputs.tf b/modules/stackit/ske/backplane/outputs.tf new file mode 100644 index 0000000..88c278c --- /dev/null +++ b/modules/stackit/ske/backplane/outputs.tf @@ -0,0 +1,50 @@ +output "cluster_name" { + description = "Name of the SKE cluster." + value = stackit_ske_cluster.this.name +} + +output "kube_host" { + description = "Kubernetes API server endpoint." + value = local.kube_host + sensitive = true +} + +output "cluster_ca_certificate" { + description = "PEM-encoded CA certificate for the cluster." + value = local.cluster_ca_certificate + sensitive = true +} + +output "client_certificate" { + description = "PEM-encoded client certificate for authentication." + value = local.client_certificate + sensitive = true +} + +output "client_key" { + description = "PEM-encoded client key for authentication." + value = local.client_key + sensitive = true +} + +output "replicator_token" { + description = "Access token for the meshStack replicator service account." + value = module.meshplatform.replicator_token + sensitive = true +} + +output "metering_token" { + description = "Access token for the meshStack metering service account." + value = module.meshplatform.metering_token + sensitive = true +} + +output "kubernetes_version" { + description = "Kubernetes version running on the cluster." + value = stackit_ske_cluster.this.kubernetes_version_used +} + +output "console_url" { + description = "URL to the STACKIT portal for this SKE cluster." + value = "https://portal.stackit.cloud/project/${stackit_ske_cluster.this.project_id}/kubernetes/${stackit_ske_cluster.this.name}" +} diff --git a/modules/stackit/ske/backplane/variables.tf b/modules/stackit/ske/backplane/variables.tf new file mode 100644 index 0000000..93ace28 --- /dev/null +++ b/modules/stackit/ske/backplane/variables.tf @@ -0,0 +1,76 @@ +variable "stackit_project_id" { + type = string + description = "STACKIT project ID where the SKE cluster will be created." +} + +variable "cluster_name" { + type = string + description = "Name of the SKE cluster." + default = "ske-cluster" +} + +variable "region" { + type = string + description = "STACKIT region for the SKE cluster." + default = "eu01" +} + +variable "node_count" { + type = number + description = "Number of nodes in the default node pool." + default = 1 +} + +variable "machine_type" { + type = string + description = "Machine type for the default node pool." + default = "c2i.2" +} + +variable "availability_zones" { + type = list(string) + description = "Availability zones for the default node pool." + default = ["eu01-1"] +} + +variable "volume_size" { + type = number + description = "Volume size in GB for nodes in the default node pool." + default = 25 +} + +variable "volume_type" { + type = string + description = "Volume type for nodes in the default node pool." + default = "storage_premium_perf0" +} + +variable "maintenance_start" { + type = string + description = "Start of the maintenance window (UTC)." + default = "02:00:00Z" +} + +variable "maintenance_end" { + type = string + description = "End of the maintenance window (UTC)." + default = "06:00:00Z" +} + +variable "enable_kubernetes_version_updates" { + type = bool + description = "Enable automatic Kubernetes version updates during maintenance windows." + default = true +} + +variable "enable_machine_image_version_updates" { + type = bool + description = "Enable automatic machine image version updates during maintenance windows." + default = true +} + +variable "meshplatform_namespace" { + type = string + description = "Kubernetes namespace for the meshStack platform integration (replicator + metering service accounts)." + default = "meshcloud" +} diff --git a/modules/stackit/ske/backplane/versions.tf b/modules/stackit/ske/backplane/versions.tf new file mode 100644 index 0000000..a2edfb0 --- /dev/null +++ b/modules/stackit/ske/backplane/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.3.0" + + required_providers { + stackit = { + source = "stackitcloud/stackit" + version = ">= 0.68.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "~> 2.0" + } + } +} diff --git a/modules/stackit/ske/forgejo-connector/buildingblock/APP_TEAM_README.md b/modules/stackit/ske/forgejo-connector/buildingblock/APP_TEAM_README.md new file mode 100644 index 0000000..b3446fd --- /dev/null +++ b/modules/stackit/ske/forgejo-connector/buildingblock/APP_TEAM_README.md @@ -0,0 +1,41 @@ +# Forgejo Actions Integration with SKE + +## Description + +This building block connects your Forgejo (STACKIT Git) repository to a STACKIT Kubernetes +Engine (SKE) namespace via Forgejo Actions. It provides your CI/CD workflows with secure +access to deploy applications into your Kubernetes namespace. + +## Usage Motivation + +Use this building block when you want to automate deployments from your STACKIT Git repository +to SKE using Forgejo Actions workflows, similar to GitHub Actions. + +## Usage Examples + +- Push code to your repository and have Forgejo Actions automatically build and deploy your + application to the connected SKE namespace. +- Set up a workflow that runs tests and deploys on merge to the main branch. + +## Prerequisites + +- A Forgejo repository (created via the STACKIT Git Repository building block) +- An SKE namespace (created via the STACKIT Starterkit or manually) + +## Shared Responsibility + +| Responsibility | Platform Team | Application Team | +|---------------------------------------------------|---------------|------------------| +| Setting up Forgejo Actions connector and secrets | ✅ | ❌ | +| Managing SKE cluster and namespace | ✅ | ❌ | +| Writing Forgejo Actions workflow files | ❌ | ✅ | +| Writing and maintaining Kubernetes manifests | ❌ | ✅ | +| Monitoring deployments and troubleshooting | ❌ | ✅ | + +## Recommendations + +- **Use namespace-scoped resources**: The service account only has `edit` access within your + namespace — do not attempt to create cluster-scoped resources. +- **Keep secrets secure**: The `KUBECONFIG` secret is automatically managed. Do not expose it + in workflow logs. +- **Use deployment strategies**: Implement rolling updates for minimal downtime. diff --git a/modules/stackit/ske/forgejo-connector/buildingblock/README.md b/modules/stackit/ske/forgejo-connector/buildingblock/README.md new file mode 100644 index 0000000..85f1eb1 --- /dev/null +++ b/modules/stackit/ske/forgejo-connector/buildingblock/README.md @@ -0,0 +1,19 @@ +--- +name: Forgejo Actions Integration with SKE +supportedPlatforms: + - stackit +description: | + CI/CD pipeline using Forgejo Actions for deploying to STACKIT Kubernetes Engine (SKE). +--- + +# Forgejo Actions Integration with SKE + +This Terraform module provisions the necessary resources to integrate Forgejo Actions +with an SKE cluster namespace. It creates a Kubernetes service account with deployment +permissions and stores the kubeconfig as a Forgejo Actions secret. + +## Features + +- Secure authentication using Kubernetes service accounts +- Kubeconfig automatically stored as a Forgejo Actions repository secret +- Namespace-scoped RBAC (edit role) for least-privilege deployments diff --git a/modules/stackit/ske/forgejo-connector/buildingblock/logo.png b/modules/stackit/ske/forgejo-connector/buildingblock/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d4ce3d0b7419dacd928ba42b4454b723535d6bdd GIT binary patch literal 3594 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 dB /tmp/kubeconfig + kubectl --kubeconfig /tmp/kubeconfig apply -f k8s/ + ``` +${var.harbor != null ? "3. Use Harbor secrets to push container images:\n ```yaml\n - name: Push to Harbor\n run: |\n echo \"$${secrets.HARBOR_TOKEN}\" | docker login $${secrets.HARBOR_URL} -u \"$${secrets.HARBOR_USERNAME}\" --password-stdin\n docker push $${secrets.HARBOR_URL}/registry/$${your-app}:latest\n ```" : ""} +EOT +} diff --git a/modules/stackit/ske/forgejo-connector/buildingblock/provider.tf b/modules/stackit/ske/forgejo-connector/buildingblock/provider.tf new file mode 100644 index 0000000..390aa91 --- /dev/null +++ b/modules/stackit/ske/forgejo-connector/buildingblock/provider.tf @@ -0,0 +1,4 @@ +provider "gitea" { + base_url = var.gitea_base_url + token = var.gitea_token +} diff --git a/modules/stackit/ske/forgejo-connector/buildingblock/variables.tf b/modules/stackit/ske/forgejo-connector/buildingblock/variables.tf new file mode 100644 index 0000000..134aaaf --- /dev/null +++ b/modules/stackit/ske/forgejo-connector/buildingblock/variables.tf @@ -0,0 +1,53 @@ +# ── Backplane inputs (static, set once per building block definition) ────────── + +variable "gitea_base_url" { + type = string + description = "STACKIT Git (Forgejo) base URL." + default = "https://git-service.git.onstackit.cloud" +} + +variable "gitea_token" { + type = string + description = "STACKIT Git API token with permissions to manage repository secrets." + sensitive = true +} + +variable "gitea_organization" { + type = string + description = "STACKIT Git organization that owns the repository." +} + +# ── Inputs wired from parent building block / platform ──────────────────────── + +variable "namespace" { + type = string + description = "SKE namespace to connect the Forgejo Actions pipeline to." +} + +variable "repository_name" { + type = string + description = "Name of the Forgejo repository to store deployment secrets in." +} + +variable "cluster_server" { + type = string + description = "SKE cluster API server URL for kubeconfig generation." +} + +variable "cluster_ca_certificate" { + type = string + description = "Base64-encoded CA certificate of the SKE cluster." +} + +# ── Optional container registry credentials ────────────────────────────────── + +variable "harbor" { + type = object({ + url = string + robot_username = string + robot_token = string + }) + sensitive = true + default = null + description = "Harbor registry credentials. When provided, stores push credentials as Forgejo Actions secrets and creates an image pull secret in the namespace." +} diff --git a/modules/stackit/ske/forgejo-connector/buildingblock/versions.tf b/modules/stackit/ske/forgejo-connector/buildingblock/versions.tf new file mode 100644 index 0000000..70d5b64 --- /dev/null +++ b/modules/stackit/ske/forgejo-connector/buildingblock/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.3.0" + + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = "~> 2.35.0" + } + gitea = { + source = "go-gitea/gitea" + version = "~> 0.7.0" + } + } +} diff --git a/modules/stackit/ske/forgejo-connector/meshstack_integration.tf b/modules/stackit/ske/forgejo-connector/meshstack_integration.tf new file mode 100644 index 0000000..249d8b7 --- /dev/null +++ b/modules/stackit/ske/forgejo-connector/meshstack_integration.tf @@ -0,0 +1,183 @@ +# This file is an example showing how to register the Forgejo Actions connector +# building block in a meshStack instance. Adapt variables to your setup. + +variable "hub" { + type = object({ + git_ref = string + }) + default = { + git_ref = "main" + } + description = "Hub release reference. Set git_ref to a tag (e.g. 'v1.2.3') or branch for the meshstack-hub repo." +} + +variable "meshstack" { + type = object({ + owning_workspace_identifier = string + }) + description = "Shared meshStack context passed down from the IaC runtime." +} + +variable "gitea" { + type = object({ + base_url = string + token = string + organization = string + }) + sensitive = true + description = "STACKIT Git (Forgejo) credentials and organization." +} + +variable "ske" { + type = object({ + cluster_server = string + cluster_ca_certificate = string + }) + description = "SKE cluster connection details for kubeconfig generation." +} + +variable "git_repo_bbd" { + type = object({ + uuid = string + }) + description = "Reference to the STACKIT Git Repository building block definition (dependency)." +} + +variable "harbor" { + type = object({ + url = string + robot_username = string + robot_token = string + }) + sensitive = true + default = null + description = "Harbor registry credentials. When provided, stores push credentials as Forgejo Actions secrets and creates an image pull secret in the namespace." +} + +terraform { + required_providers { + meshstack = { + source = "meshcloud/meshstack" + version = ">= 0.19.3" + } + } +} + +resource "meshstack_building_block_definition" "forgejo_connector" { + metadata = { + owned_by_workspace = var.meshstack.owning_workspace_identifier + } + + spec = { + description = "CI/CD pipeline using Forgejo Actions for deploying to STACKIT Kubernetes Engine (SKE)." + display_name = "Forgejo Actions Integration with SKE" + symbol = provider::meshstack::load_image_file("${path.module}/buildingblock/logo.png") + target_type = "TENANT_LEVEL" + supported_platforms = [{ name = "STACKIT_KUBERNETES_ENGINE" }] + run_transparency = true + readme = file("${path.module}/buildingblock/README.md") + } + + version_spec = { + draft = true + implementation = { + terraform = { + repository_url = "https://github.com/meshcloud/meshstack-hub.git" + terraform_version = "1.9.0" + async = false + ref_name = var.hub.git_ref + repository_path = "modules/stackit/ske/forgejo-connector/buildingblock" + use_mesh_http_backend_fallback = true + } + } + dependency_refs = [ + { uuid = var.git_repo_bbd.uuid } + ] + inputs = merge({ + "gitea_base_url" = { + display_name = "STACKIT Git Base URL" + description = "Base URL of the STACKIT Git (Forgejo) instance." + type = "STRING" + assignment_type = "STATIC" + argument = jsonencode(var.gitea.base_url) + } + "gitea_token" = { + display_name = "STACKIT Git API Token" + description = "API token for managing Forgejo repository secrets." + type = "STRING" + assignment_type = "STATIC" + sensitive = { + argument = { + secret_value = var.gitea.token + } + } + } + "gitea_organization" = { + display_name = "STACKIT Git Organization" + description = "Organization that owns the repository." + type = "STRING" + assignment_type = "STATIC" + argument = jsonencode(var.gitea.organization) + } + "repository_name" = { + display_name = "Repository Name" + description = "Name of the Forgejo repository (wired from Git Repository BBD output)." + type = "STRING" + assignment_type = "BUILDING_BLOCK_OUTPUT" + argument = jsonencode("${var.git_repo_bbd.uuid}.repository_name") + } + "namespace" = { + display_name = "SKE Namespace" + type = "STRING" + assignment_type = "PLATFORM_TENANT_ID" + } + "cluster_server" = { + display_name = "SKE Cluster Server" + description = "API server URL of the SKE cluster." + type = "STRING" + assignment_type = "STATIC" + argument = jsonencode(var.ske.cluster_server) + } + "cluster_ca_certificate" = { + display_name = "SKE Cluster CA Certificate" + description = "Base64-encoded CA certificate of the SKE cluster." + type = "STRING" + assignment_type = "STATIC" + argument = jsonencode(var.ske.cluster_ca_certificate) + } + }, var.harbor != null ? { + "harbor" = { + display_name = "Harbor Registry Credentials" + description = "Harbor registry URL and robot account credentials for container image push/pull." + type = "CODE" + assignment_type = "STATIC" + sensitive = { + argument = { + secret_value = jsonencode({ + url = var.harbor.url + robot_username = var.harbor.robot_username + robot_token = var.harbor.robot_token + }) + } + } + } + } : {}) + outputs = { + "summary" = { + display_name = "Summary" + type = "STRING" + assignment_type = "SUMMARY" + } + } + } +} + +output "bbd_uuid" { + description = "UUID of the Forgejo Actions connector building block definition." + value = meshstack_building_block_definition.forgejo_connector.ref.uuid +} + +output "bbd_version_uuid" { + description = "UUID of the latest version of the Forgejo Actions connector building block definition." + value = meshstack_building_block_definition.forgejo_connector.version_latest.uuid +} diff --git a/modules/stackit/ske/meshstack_integration.tf b/modules/stackit/ske/meshstack_integration.tf new file mode 100644 index 0000000..dc6f18c --- /dev/null +++ b/modules/stackit/ske/meshstack_integration.tf @@ -0,0 +1,158 @@ +# Adapt these variables to your STACKIT and meshStack setup. + +variable "hub" { + type = object({ + git_ref = string + }) + default = { + git_ref = "main" + } + description = "Hub release reference. Set git_ref to a tag (e.g. 'v1.2.3') or branch for the meshstack-hub repo." +} + +variable "meshstack" { + type = object({ + owning_workspace_identifier = string + }) + description = "Shared meshStack context passed down from the IaC runtime." +} + +variable "ske" { + type = object({ + platform_identifier = string + location_identifier = string + + # Cluster connection + base_url = string + disable_ssl_validation = optional(bool, true) + + # Replication + namespace_name_pattern = optional(string, "#{workspaceIdentifier}-#{projectIdentifier}") + }) + description = "STACKIT Kubernetes Engine platform configuration." +} + +module "backplane" { + source = "./backplane" + + stackit_project_id = var.stackit_project_id + cluster_name = var.cluster_name + region = var.region +} + +variable "stackit_project_id" { + type = string + description = "STACKIT project ID where the SKE cluster will be created." +} + +variable "cluster_name" { + type = string + description = "Name of the SKE cluster." + default = "ske-cluster" +} + +variable "region" { + type = string + description = "STACKIT region for the SKE cluster." + default = "eu01" +} + +resource "meshstack_platform" "ske" { + metadata = { + name = var.ske.platform_identifier + owned_by_workspace = var.meshstack.owning_workspace_identifier + } + + spec = { + description = "STACKIT Kubernetes Engine (SKE). Create a k8s namespace in our SKE cluster." + display_name = "SKE Namespace" + endpoint = var.ske.base_url + + location_ref = { + name = var.ske.location_identifier + } + + availability = { + restriction = "PUBLIC" + publication_state = "PUBLISHED" + } + + config = { + kubernetes = { + base_url = var.ske.base_url + disable_ssl_validation = var.ske.disable_ssl_validation + + replication = { + client_config = { + access_token = { + secret_value = module.backplane.replicator_token + secret_version = sha256(module.backplane.replicator_token) + } + } + namespace_name_pattern = var.ske.namespace_name_pattern + } + + metering = { + client_config = { + access_token = { + secret_value = module.backplane.metering_token + secret_version = sha256(module.backplane.metering_token) + } + } + processing = {} + } + } + } + } +} + +resource "meshstack_landingzone" "ske_default" { + metadata = { + name = "${var.ske.platform_identifier}-default" + owned_by_workspace = var.meshstack.owning_workspace_identifier + } + + spec = { + description = "Default SKE landing zone" + display_name = "SKE Default" + + platform_ref = meshstack_platform.ske.metadata + + automate_deletion_approval = true + automate_deletion_replication = true + + platform_properties = { + kubernetes = { + kubernetes_role_mappings = [ + { + platform_roles = ["admin"] + project_role_ref = { + name = "admin" + } + }, + { + platform_roles = ["edit"] + project_role_ref = { + name = "user" + } + }, + { + platform_roles = ["view"] + project_role_ref = { + name = "reader" + } + } + ] + } + } + } +} + +terraform { + required_providers { + meshstack = { + source = "meshcloud/meshstack" + version = "~> 0.19.1" + } + } +} diff --git a/modules/stackit/ske/starterkit/backplane/main.tf b/modules/stackit/ske/starterkit/backplane/main.tf new file mode 100644 index 0000000..874e4d2 --- /dev/null +++ b/modules/stackit/ske/starterkit/backplane/main.tf @@ -0,0 +1,22 @@ +module "git_repo_bbd" { + source = "../../../git-repository" + + owning_workspace_identifier = var.meshstack.owning_workspace_identifier + meshstack_hub_git_ref = var.hub.git_ref + gitea_base_url = var.gitea.base_url + gitea_token = var.gitea.token + gitea_organization = var.gitea.organization +} + +module "forgejo_connector_bbd" { + source = "../../forgejo-connector" + + hub = var.hub + meshstack = var.meshstack + gitea = var.gitea + ske = var.ske + harbor = var.harbor + git_repo_bbd = { + uuid = module.git_repo_bbd.bbd_uuid + } +} diff --git a/modules/stackit/ske/starterkit/backplane/outputs.tf b/modules/stackit/ske/starterkit/backplane/outputs.tf new file mode 100644 index 0000000..05f8c18 --- /dev/null +++ b/modules/stackit/ske/starterkit/backplane/outputs.tf @@ -0,0 +1,14 @@ +output "git_repo_bbd_uuid" { + description = "UUID of the STACKIT Git repository building block definition." + value = module.git_repo_bbd.bbd_uuid +} + +output "git_repo_bbd_version_uuid" { + description = "UUID of the latest version of the STACKIT Git repository building block definition." + value = module.git_repo_bbd.bbd_version_uuid +} + +output "forgejo_connector_bbd_version_uuid" { + description = "UUID of the latest version of the Forgejo Actions connector building block definition." + value = module.forgejo_connector_bbd.bbd_version_uuid +} diff --git a/modules/stackit/ske/starterkit/backplane/variables.tf b/modules/stackit/ske/starterkit/backplane/variables.tf new file mode 100644 index 0000000..4b64faa --- /dev/null +++ b/modules/stackit/ske/starterkit/backplane/variables.tf @@ -0,0 +1,42 @@ +variable "hub" { + type = object({ + git_ref = string + }) + description = "Hub release reference. Set git_ref to a tag (e.g. 'v1.2.3') or branch for the meshstack-hub repo." +} + +variable "meshstack" { + type = object({ + owning_workspace_identifier = string + }) + description = "Shared meshStack context passed down from the IaC runtime." +} + +variable "gitea" { + type = object({ + base_url = string + token = string + organization = string + }) + sensitive = true + description = "STACKIT Git (Forgejo) credentials and organization." +} + +variable "ske" { + type = object({ + cluster_server = string + cluster_ca_certificate = string + }) + description = "SKE cluster connection details for kubeconfig generation in the Forgejo connector." +} + +variable "harbor" { + type = object({ + url = string + robot_username = string + robot_token = string + }) + sensitive = true + default = null + description = "Harbor registry credentials. When provided, stores push credentials as Forgejo Actions secrets and creates an image pull secret in each namespace." +} diff --git a/modules/stackit/ske/starterkit/backplane/versions.tf b/modules/stackit/ske/starterkit/backplane/versions.tf new file mode 100644 index 0000000..6c6a9bf --- /dev/null +++ b/modules/stackit/ske/starterkit/backplane/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.3.0" + + required_providers { + meshstack = { + source = "meshcloud/meshstack" + version = ">= 0.19.3" + } + } +} diff --git a/modules/stackit/ske/starterkit/buildingblock/APP_TEAM_README.md b/modules/stackit/ske/starterkit/buildingblock/APP_TEAM_README.md new file mode 100644 index 0000000..886afd3 --- /dev/null +++ b/modules/stackit/ske/starterkit/buildingblock/APP_TEAM_README.md @@ -0,0 +1,41 @@ +# STACKIT SKE Starterkit — Application Team Guide + +## What does this building block do? + +The STACKIT SKE Starterkit provisions a complete development environment for your team: + +- **Git Repository** on STACKIT Git (Forgejo/Gitea) for your application source code +- **Dev Namespace** on STACKIT Kubernetes Engine for development workloads +- **Prod Namespace** on STACKIT Kubernetes Engine for production workloads + +## When to use it + +Use this starterkit when your team needs to deploy containerized applications on a sovereign +European Kubernetes platform with separate dev and prod environments. + +## Usage + +1. Request the starterkit from the meshStack marketplace +2. Provide a project name — this will be used for naming all resources +3. Once provisioned, clone the Git repository and start developing +4. Set up your own CI/CD pipeline to deploy to the dev and prod namespaces + +## Shared Responsibility Matrix + +| Responsibility | Platform Team | Application Team | +| --------------------------------------- | ------------- | ---------------- | +| Provision and manage SKE cluster | ✅ | ❌ | +| Create Git repository | ✅ | ❌ | +| Create Kubernetes namespaces | ✅ | ❌ | +| Manage access to projects | ✅ | ✅ | +| Deploy applications to namespaces | ❌ | ✅ | +| Develop and maintain application code | ❌ | ✅ | +| Set up CI/CD pipelines | ❌ | ✅ | +| Monitor application health | ❌ | ✅ | + +## Best Practices + +- Use the **dev** namespace for testing before promoting to **prod** +- Set up branch protection rules in the Git repository +- Configure resource limits in your Kubernetes deployments +- Use Kubernetes Secrets for sensitive configuration diff --git a/modules/stackit/ske/starterkit/buildingblock/README.md b/modules/stackit/ske/starterkit/buildingblock/README.md new file mode 100644 index 0000000..41e72c1 --- /dev/null +++ b/modules/stackit/ske/starterkit/buildingblock/README.md @@ -0,0 +1,18 @@ +--- +name: STACKIT SKE Starterkit +supportedPlatforms: + - stackit +description: One-click dev/prod environment with STACKIT Git repository and SKE Kubernetes namespaces. +--- + +# STACKIT SKE Starterkit + +This building block creates a complete dev/prod environment on STACKIT Kubernetes Engine (SKE), +including a Git repository on STACKIT Git and dedicated Kubernetes namespaces. + +## Resources Created + +- 2 meshStack projects (dev + prod) +- 2 SKE tenants (Kubernetes namespaces via meshStack replicator) +- 1 STACKIT Git repository (Forgejo/Gitea) +- Project Admin bindings for the creator diff --git a/modules/stackit/ske/starterkit/buildingblock/logo.png b/modules/stackit/ske/starterkit/buildingblock/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d4ce3d0b7419dacd928ba42b4454b723535d6bdd GIT binary patch literal 3594 zcmY*c2|SeR_n#RY-Q;p{a@Yw{r>;w{XEY(&w0-Io^zh_exCPvlh0Y3?uARjK_Jjx zf|-#mK%0P`1``6tMb7TG00C2MO$|USuJjBr`H#O7foNp~QUZ7wND#aqB(S3bJRq8<3DvEAkbA32>MgU1)x9^_yi1q^-n5L3jQx*Ddd-SQ>nl&K5fUY zyu4}{Ai`m0&J++R|JhClk7t!50W@cly%W{R$`a=r8m!{&7wY4$LJtnxu>xt+aR3|a zPxVI7gUKNj937ARp@9SV9Wfe-_@P1#!Xurm&LIp#!~GGbRa8|}kveb$0-+u5N5t70 znfx>dW_aWUDm4s;Mn^_Qszj=*goX#8F<2}Xt*VArQ&R>slqnZOsNQtt5Q^NtApeVF zQ0QU)!bwKjJaA3k2HTNHEg3 zr-SF+`!b|W55~OW_%dof8tFR;C&&Ac4fF`)ae3)SVIb5Y6G^NK^jbKuAP`{97;lTI z-lfvcMOt)K9d!OUAq7vzj7H~)xaJ;eW*Gd^P|hZNN@?$_yA$;&e~_58P`m_9#@ozh8-$l%sqlEotQ>>+ zr95X?>pER5jto`9F}IB``FWCCXD3ecT-0vB=ujaorik5YT!&(+PQk0=HFXas+a6mu zE6i5%e50&6d%9~)VGJdl4Cn;)L6lIZp?tVb<{pptL zu)M9c#+a(B{G6O%W-l16fktLt(FI9>{+Ux ztfvA|{E#pHcwkI_RsCpMwk%OE*Sinq*H5*C-!3BtFz_Vx&RwF_k&{y~I!Dc{6+R&t z8FH8tP0RHn;4ile@x${L&l(2wX>x@#dEVNJg3#9sg&SvMZE3PvYMk1@}^_uUN4<9W!|s@OS*R?|E#M{a#Mxh^Oc&30vBm8)fC zjTIyPZ6&42>`0t4YN@^UIZ*bKFekgSgTGJW54Y2ll1 z?MPSnn_i%fvDgP2&HmP0uy1z?C<{m<2G8+4$m-6{hQH_MHhmn(SzF_)x%(|C404W!6ROH|yW z#pGRoV)azMDRHo}EWFY;%t|*Zdo1fxpi=nw+*kFZGuZR%LG5yzk9t0Y6%D$-F>8Fq ze>kPvo}2aJj#!%e>V#gw*J3OSJfpWYca zUUk>@)Wl&o#xku^PImi zm#3*mD|xE}-a!_r#RiR#yvBL$zDbL#U* zQQ0m{WCYimS9-2C4Tap~`9J(#HoF+sHNpXsF;?l$AAflNOotGKPp$91vKH}>{`;m7 zrDB@n-oHQ*{8m}~H5_$-Gtk-vSN!V)YSRb zKKwX*>8S2Rk9n$k9pnmMvXB6&-_x(M0e^x}e?L!P7+bflAGww);WtzrEaWn6-@!kp z+ItJN896dC76@t;(OUCw!!c_T$LD{?F^k>$a=tAmTv_O+OKA-L*>a3YWchSUjLY5> ze(AAZFR)Z<^0mlLBjCkYGM*LxCT(@xg5W!!of`Q)+dO!s{-0a!3F^XCl()9k57P5m zpLOS~OZi9MUz+NcA>}E?zFMPRlHNN$=e4il?lAwuTy%HxQuOSuB-$J;g4!gEaCeBC z7VpXzw;Uq(jf9L1oqN4)IHJ3J5mdDt7S5i(>mYr>Exis}J!xI!ak4K@`&~M0BXroV_nH(L7aVTC?R43>1CHF~#D5*);^3z#{x|#<=vkPv-jL7rS zT<>M*4i3a)b2RAI#-qRid~;A5D8ukH5qG)fb4h635uMCC1&1s9!gC%u3l`fwKL9PD zD5X86u-|OVDq88j>o2Qe6=J`5H6&EYMYfJmk6i{|+P*0znefi&K9-Hvl4ezPyo`>h z`F4M<@br49TPN_PY0OU_>S=aa*Lu_TDorT*BHUWkXzn?RY|cit#byNNxg5jzO}`w@ z^}8BCH_(#<-bel6Ihm(ZZ~4MfYA%qtMVsiSbwed8%fy@q)XDZY35tTpl?pT&VmPz@ zPI1d^cKC1GOAGGuZ?f5$e1b;lxFn95$1V4A$i^e$WIx*65{OU~9L(l}g(s>9N!aD3 zjGk0X5uv@7Un(S@*>R`dGxPBQXQMOyGxhvW9tOEtnp*6Lc(M`^cZ(8o!~>m_Dv`}k zav=1SkI@;W_@jeM35*?R=|Cs1y?jgfT?L-N{)(}zNST~B%}Gil*W5o8;6e;Vps{I*2PA*=@j{K6WK@{4O&%nlb8Q!?2v8n0#9?ZgOl!OQ)i5M zsGOh&r6CEzVVrK(SXJ5nXUjK^A-(oa)W0;A49Icsa5gn|S>`Q?RYw&Pz^P<*kdvyY zhL@8r2DCoD*J>om90qRiQ1y*4ES)C0-x6Bh(vp9*I3prjl8l;D`ss%xu!f}jLYtukMML2V<4Zmy!G zjESYEz8-U0BdOJVEw6YG1n;nfzGJ0_oB=-_QsODB^+|XbAw=R@dp1!z{fvrZmoB~@ z-#Opv9=l#?yIzBVzQpA~e0nsS5(VzZ|Fx$;UvHo|{US&i*!Y=!7bR6&WT525D^YMs zj}sYPAs(_GV!h6~5So7d`u1J-3^PU4un!+Rf9>2hP_tga$kDcsq81ejb7oVEEfc^s zs_uHH0TuataNZJLYU<;2+8ZLdha;xiSVYi2VMAMJmwE1$m!@~pL6`sOV2XT54YB_; z#tsj8z3piIZS9!@r~`ZBV@Wbb*k;z@Tnf%UQO|E{TeY6Ol_w^KfEdNXM8QZsXcFk> dB= 1.3.0" + + required_providers { + meshstack = { + source = "meshcloud/meshstack" + version = ">= 0.19.3" + } + random = { + source = "hashicorp/random" + version = ">= 3.0" + } + } +} diff --git a/modules/stackit/ske/starterkit/meshstack_integration.tf b/modules/stackit/ske/starterkit/meshstack_integration.tf new file mode 100644 index 0000000..2de40a4 --- /dev/null +++ b/modules/stackit/ske/starterkit/meshstack_integration.tf @@ -0,0 +1,248 @@ +# Adapt these variables to your meshStack and STACKIT setup. + +variable "hub" { + type = object({ + git_ref = string + }) + default = { + git_ref = "main" + } + description = "Hub release reference. Set git_ref to a tag (e.g. 'v1.2.3') or branch for the meshstack-hub repo." +} + +variable "meshstack" { + type = object({ + owning_workspace_identifier = string + }) + description = "Shared meshStack context passed down from the IaC runtime." +} + +variable "ske" { + description = "SKE platform identifiers." + type = object({ + full_platform_identifier = string + landing_zone_dev_identifier = string + landing_zone_prod_identifier = string + cluster_server = string + cluster_ca_certificate = string + }) +} + +variable "gitea" { + type = object({ + base_url = string + token = string + organization = string + }) + sensitive = true + description = "STACKIT Git (Forgejo) credentials and organization." +} + +variable "harbor" { + type = object({ + url = string + robot_username = string + robot_token = string + }) + sensitive = true + default = null + description = "Harbor registry credentials. When provided, stores push credentials as Forgejo Actions secrets and creates an image pull secret in each namespace." +} + +variable "project_tags_yaml" { + description = "YAML string defining tags for created projects." + type = string + default = <<-YAML +dev: + environment: + - "dev" +prod: + environment: + - "prod" +YAML +} + +terraform { + required_providers { + meshstack = { + source = "meshcloud/meshstack" + version = ">= 0.19.3" + } + } +} + +module "backplane" { + source = "./backplane" + + hub = var.hub + meshstack = var.meshstack + gitea = var.gitea + harbor = var.harbor + ske = { + cluster_server = var.ske.cluster_server + cluster_ca_certificate = var.ske.cluster_ca_certificate + } +} + +resource "meshstack_building_block_definition" "stackit_starterkit" { + metadata = { + owned_by_workspace = var.meshstack.owning_workspace_identifier + } + + spec = { + description = "The STACKIT Starterkit provides application teams with a pre-configured Kubernetes environment on STACKIT Kubernetes Engine (SKE). It includes a Git repository on STACKIT Git, dedicated dev/prod SKE namespaces, and Forgejo Actions CI/CD integration." + display_name = "STACKIT Starterkit" + symbol = provider::meshstack::load_image_file("${path.module}/buildingblock/logo.png") + readme = chomp(<