Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ yarn-error.log*
*tfvars*
.terraform.lock.hcl
.env
__pycache__/
*.pyc

# Generated assets
website/public/assets/building-block-logos/
Expand Down
38 changes: 0 additions & 38 deletions modules/ske/forgejo-connector/backplane/README.md

This file was deleted.

11 changes: 0 additions & 11 deletions modules/ske/forgejo-connector/backplane/main.tf

This file was deleted.

30 changes: 0 additions & 30 deletions modules/ske/forgejo-connector/backplane/outputs.tf

This file was deleted.

28 changes: 0 additions & 28 deletions modules/ske/forgejo-connector/backplane/variables.tf

This file was deleted.

10 changes: 0 additions & 10 deletions modules/ske/forgejo-connector/backplane/versions.tf

This file was deleted.

This file was deleted.

85 changes: 53 additions & 32 deletions modules/ske/forgejo-connector/buildingblock/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,76 +8,97 @@ description: |

# Forgejo Actions Integration with STACKIT Kubernetes

This Terraform module provisions the necessary resources to integrate Forgejo Actions with a STACKIT Kubernetes cluster.
It sets up a service account and repository action secrets for seamless CI/CD.
This building block connects a Forgejo repository with a tenant namespace on a
STACKIT Kubernetes Engine (SKE) cluster. It provisions the Kubernetes resources
(service account, RBAC, image-pull secrets) and configures the matching Forgejo
Actions secrets and variables so that a CI/CD pipeline can deploy into the
namespace.

## Features

- Secure authentication using a Kubernetes service account and Forgejo action secrets
- STACKIT Container Registry integration for image building and pushing
- **Kubernetes service account & RBAC** – scoped credentials for the Forgejo
Actions runner, including cluster-issuer read access for cert-manager.
- **Action secrets & variables** – per-stage `KUBECONFIG_<STAGE>`,
`K8S_NAMESPACE_<STAGE>` and `APP_HOSTNAME_<STAGE>` managed via the shared
[`action-variables-and-secrets`](https://github.com/meshcloud/meshstack-hub/tree/feature/ske-starter-kit-harbor-integration/modules/stackit/git-repository/buildingblock/action-variables-and-secrets)
sub-module.
- **Harbor image-pull secret** – `kubernetes.io/dockerconfigjson` secret
attached to the default service account so pods can pull from STACKIT Harbor.
- **Pipeline trigger** – after provisioning, automatically triggers the Forgejo
Actions pipeline workflow and waits for it to complete.
- **Additional secrets** – optional map of arbitrary Opaque secrets injected
into the namespace (e.g. AI service keys).

## Providers
## Why `restapi` is used for Action secrets & variables

```hcl
terraform {
required_providers {
forgejo = {
source = "svalabs/forgejo"
version = "1.3.1"
}
Action secrets and variables are managed by the shared
[`action-variables-and-secrets`](https://github.com/meshcloud/meshstack-hub/tree/feature/ske-starter-kit-harbor-integration/modules/stackit/git-repository/buildingblock/action-variables-and-secrets)
sub-module (sourced from `git-repository`) using the generic `restapi` provider.
The Forgejo Terraform provider currently cannot delete secrets (only removes
them from state) and does not support action variables at all.

kubernetes = {
source = "hashicorp/kubernetes"
version = "2.35.1"
}
}
}
```
## Provider configuration

To setup the Forgejo provider make sure to set these environment variables for this Building Block:
`FORGEJO_HOST`, `FORGEJO_API_TOKEN`.
The module expects the following environment variables for the Forgejo provider
and the restapi providers:

The Kubernetes provider should be set up with a static file input `config.tf` with the contents of
the backplane module's `config_tf` output.
| Variable | Description |
|---------------------|-------------|
| `FORGEJO_HOST` | Base URL of the Forgejo instance. |
| `FORGEJO_API_TOKEN` | API token for authenticating with Forgejo. |

The Kubernetes provider is configured from a `kubeconfig.yaml` static file
input containing admin credentials to the SKE cluster.

<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_external"></a> [external](#requirement\_external) | ~> 2.3.0 |
| <a name="requirement_forgejo"></a> [forgejo](#requirement\_forgejo) | ~> 1.3.0 |
| <a name="requirement_kubernetes"></a> [kubernetes](#requirement\_kubernetes) | 2.35.1 |
| <a name="requirement_random"></a> [random](#requirement\_random) | ~> 3.8.0 |
| <a name="requirement_restapi"></a> [restapi](#requirement\_restapi) | ~> 3.0.0 |

## Modules

No modules.
| Name | Source | Version |
|------|--------|---------|
| <a name="module_action_secrets_and_variables"></a> [action\_secrets\_and\_variables](#module\_action\_secrets\_and\_variables) | github.com/meshcloud/meshstack-hub//modules/stackit/git-repository/buildingblock/action-variables-and-secrets | feature/ske-starter-kit-harbor-integration |

## Resources

| Name | Type |
|------|------|
| [forgejo_repository_action_secret.additional](https://registry.terraform.io/providers/svalabs/forgejo/latest/docs/resources/repository_action_secret) | resource |
| [forgejo_repository_action_secret.container_registry](https://registry.terraform.io/providers/svalabs/forgejo/latest/docs/resources/repository_action_secret) | resource |
| [forgejo_repository_action_secret.kubeconfig](https://registry.terraform.io/providers/svalabs/forgejo/latest/docs/resources/repository_action_secret) | resource |
| [kubernetes_cluster_role.clusterissuer_reader](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/cluster_role) | resource |
| [kubernetes_cluster_role_binding.forgejo_actions_clusterissuer_access](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/cluster_role_binding) | resource |
| [kubernetes_default_service_account.this](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/default_service_account) | resource |
| [kubernetes_role_binding.forgejo_actions](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/role_binding) | resource |
| [kubernetes_secret.additional](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/secret) | resource |
| [kubernetes_secret.forgejo_actions](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/secret) | resource |
| [kubernetes_secret.image_pull](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/secret) | resource |
| [kubernetes_service_account.forgejo_actions](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/service_account) | resource |
| [forgejo_repository.this](https://registry.terraform.io/providers/svalabs/forgejo/latest/docs/data-sources/repository) | data source |
| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |
| [terraform_data.await_pipeline_workflow](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource |
| [external_external.env](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_additional_environment_variables"></a> [additional\_environment\_variables](#input\_additional\_environment\_variables) | Map of additional environment variable key/value pairs to set as Forgejo repository action secrets. | `map(string)` | `{}` | no |
| <a name="input_forgejo_repository_name"></a> [forgejo\_repository\_name](#input\_forgejo\_repository\_name) | The name of the Forgejo repository. | `string` | n/a | yes |
| <a name="input_forgejo_repository_owner"></a> [forgejo\_repository\_owner](#input\_forgejo\_repository\_owner) | The owner of the Forgejo repository. | `string` | n/a | yes |
| <a name="input_additional_kubernetes_secrets"></a> [additional\_kubernetes\_secrets](#input\_additional\_kubernetes\_secrets) | Additional Kubernetes secrets to create in the tenant namespace. Map keys are secret names, values are secret data maps. | `map(map(string))` | `{}` | no |
| <a name="input_app_hostname"></a> [app\_hostname](#input\_app\_hostname) | Public application hostname for this stage (used by deploy workflow and ingress). | `string` | n/a | yes |
| <a name="input_harbor_host"></a> [harbor\_host](#input\_harbor\_host) | The URL of the Harbor registry. | `string` | `"https://registry.onstackit.cloud"` | no |
| <a name="input_harbor_password"></a> [harbor\_password](#input\_harbor\_password) | The password for the Harbor registry. | `string` | n/a | yes |
| <a name="input_harbor_username"></a> [harbor\_username](#input\_harbor\_username) | The username for the Harbor registry. | `string` | n/a | yes |
| <a name="input_namespace"></a> [namespace](#input\_namespace) | Associated namespace in kubernetes cluster. | `string` | n/a | yes |
| <a name="input_repository_id"></a> [repository\_id](#input\_repository\_id) | The ID of the Forgejo repository. | `number` | n/a | yes |
| <a name="input_stage"></a> [stage](#input\_stage) | Deployment stage used for Forgejo workflow dispatch and action secret naming. | `string` | n/a | yes |

## Outputs

No outputs.
| Name | Description |
|------|-------------|
| <a name="output_app_link"></a> [app\_link](#output\_app\_link) | Public URL for this stage application. |
<!-- END_TF_DOCS -->
86 changes: 48 additions & 38 deletions modules/ske/forgejo-connector/buildingblock/forgejo.tf
Original file line number Diff line number Diff line change
@@ -1,55 +1,65 @@
provider "forgejo" {
# configured via env variables FORGEJO_HOST, FORGEJO_API_TOKEN
}

locals {
kubeconfig_user = {
users = [
{
action_variables = {
"K8S_NAMESPACE_${upper(var.stage)}" = var.namespace
"APP_HOSTNAME_${upper(var.stage)}" = var.app_hostname
}
action_secrets = {
"KUBECONFIG_${upper(var.stage)}" = yamlencode(merge(local.kubeconfig, {
current-context = local.kubeconfig_cluster_name
# Note: Overwriting the users is crucial here to avoid passing down the admin user to the tenant-sliced K8s slices.
users = [{
name = kubernetes_service_account.forgejo_actions.metadata[0].name
user = {
"token" = kubernetes_secret.forgejo_actions.data.token
}
}
]

contexts = [
{
name = "stackit_k8s"
}]
contexts = [{
name = local.kubeconfig_cluster_name
context = {
cluster = "stackit_k8s"
cluster = local.kubeconfig_cluster_name
namespace = var.namespace
user = kubernetes_service_account.forgejo_actions.metadata[0].name
}
}
]
}]
}))
}
kubeconfig = merge(local.stackit_kubeconfig_stub, local.kubeconfig_user)
}

data "forgejo_repository" "this" {
name = var.forgejo_repository_name
owner = var.forgejo_repository_owner
}

resource "forgejo_repository_action_secret" "kubeconfig" {
repository_id = data.forgejo_repository.this.id
name = "KUBECONFIG"
data = yamlencode(local.kubeconfig)
}

resource "forgejo_repository_action_secret" "container_registry" {
for_each = {
HOST = var.harbor_host
USERNAME = var.harbor_username
PASSWORD = var.harbor_password
module "action_secrets_and_variables" {
source = "github.com/meshcloud/meshstack-hub//modules/stackit/git-repository/buildingblock/action-variables-and-secrets?ref=feature/ske-starter-kit-harbor-integration"
providers = {
restapi.action_variable = restapi.action_variable
restapi.action_secret = restapi.action_secret
}

repository_id = data.forgejo_repository.this.id
name = "STACKIT_HARBOR_${each.key}"
data = each.value
repository_id = var.repository_id
action_variables = local.action_variables
action_secrets = local.action_secrets
}

resource "forgejo_repository_action_secret" "additional" {
for_each = var.additional_environment_variables
resource "terraform_data" "await_pipeline_workflow" {
depends_on = [
module.action_secrets_and_variables,
]

triggers_replace = [
sha256(file("${path.module}/trigger_and_await_forgejo_workflow.py")),
nonsensitive(sha256(jsonencode(local.action_secrets))),
sha256(jsonencode(local.action_variables)),
]

repository_id = data.forgejo_repository.this.id
name = each.key
data = each.value
}
provisioner "local-exec" {
command = "${path.module}/trigger_and_await_forgejo_workflow.py"
environment = {
REPOSITORY_ID = tostring(var.repository_id)
WORKFLOW_NAME = "pipeline.yaml"
WORKFLOW_ONLY_STAGE = var.stage
EXPECTED_WORKFLOW_TASK_NAME = "deploy_${var.stage}"
WORKFLOW_RUN_TITLE = "Triggered by meshStack Forgejo Connector ${title(var.stage)}"
}
}
}
18 changes: 18 additions & 0 deletions modules/ske/forgejo-connector/buildingblock/kubeconfig-mock.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This mock kubeconfig is only used as a fallback when kubeconfig.yaml is not
# injected by meshStack yet (e.g. local terraform validate).
current-context: mock-context
clusters:
- name: mock-cluster
cluster:
server: https://example.invalid # This must not be changed to avoid accidental usage via precondition check
certificate-authority-data: ""
users:
- name: mock-user
user:
client-certificate-data: ""
client-key-data: ""
contexts:
- name: mock-context
context:
cluster: mock-cluster
user: mock-user
Loading
Loading