Themelio compatibility reference. This page describes the retained Themelio implementation or its historical design. Check the parallel-operation guide before applying it to the current platform.
DD-0006: Automated Seeding Strategy — Implementation
Related ADR: ADR-0006: Automated Data Seeding Strategy
This document provides the full technical specification for implementing the automated seeding strategy described in ADR-0006. It is intended to be followed by an engineer or an automated agent.
1. Kubernetes Seeder Job
A single-run Kubernetes Job deployed by Ansible. The Job is limited to Git repository mirroring; it does not run Waldur orchestration, does not poll site-agent logs, and does not need Kubernetes API access.
Container design
- Base image:
alpine:latest(no pre-installed tools required) - Main container: installs required tools at startup with
apk add --no-cache bash git git-lfs curl jq, then runs the embedded repository mirroring script. - Ephemeral storage: an
emptyDirvolume at/workspace(defaultsizeLimit: 10Gi) is used for cloning repositories. Each repository directory is deleted after pushing to keep peak usage bounded. - Script delivery: Ansible renders
seeder-job.yml.j2, which produces a complete Kubernetes Job manifest with the bash script embedded directly in.spec.template.spec.containers[0].commandand runtime values passed through.spec.template.spec.containers[0].env. There is no ConfigMap or separate seeder credentials Secret — the final Kubernetes resource contains the script and container environment. - Kubernetes access: The Job sets
automountServiceAccountToken: falseand does not define a custom ServiceAccount, Role, or RoleBinding.
Workflow
The workflow is split between the Ansible role and the Kubernetes Job.
Ansible-controlled Waldur orchestration
-
Reject a stale seeder Job. Before starting, check whether
Job/themelio-seederalready exists in thethemelionamespace. If it exists, fail clearly and leave the existing Job untouched. -
Validate the external GitLab token. Read
SEEDER_EXTERNAL_GITLAB_TOKENfrom the local environment of the operator running Ansible. Validate it withGET https://gitlab.grnet.gr/api/v4/user. If it is missing, invalid, or expired, fail before creating any Kubernetes Job. -
Run Waldur operations through
kubectl exec. The role useskubectl -n waldur exec -i deploy/waldur-mastermind-worker -- waldur shellso Waldur-side operations run inside the application context. -
Validate the Waldur organization exists. Look up an organization named
pharos. If it exists, retrieve and store its UUID for use in subsequent steps. If it does not exist, fail with:organization does not exist, run waldur-offering target first. -
Ensure
pharos-adminexists in Waldur. Create or update the user if needed. This user is a Waldur staff user, has a password from vault, and is granted access to the seeded project. -
Ensure the Waldur project exists. Create or reuse the project and grant
pharos-adminproject-level access. The project name and project slug are bothpharos. -
Place or reuse the order. Look up the Cloud offering by the shared
waldur_offering_nameinventory variable and use the resulting UUID to initiate or reuse an order. The role only reuses an existing order/resource for the configured allocation name if the order is not a terminate order and the linked resource is not terminated, terminating, erred, or deleted. Terminated resources and terminate orders are ignored so a fresh create order can be placed. The order uses these values:The role creates the order through Waldur's in-process DRF API client, using compact UUID hyperlinks. It obtains the plan URL from the serialized
marketplace-public-offeringsresponse instead of constructing the plan URL by hand, setsaccepting_terms_of_service: true, and attemptsapprove_by_providerandapprove_by_consumerafter order creation. The returned order ID is cached as an Ansible fact for the current run. Subsequent runs inspect existing Waldur resources and orders instead of relying on stored local state. This order triggers thewaldur-k8s-site-agentto begin provisioning GitLab subgroups, users, and the Kubernetes namespace with resource quotas. -
Wait for
waldur-k8s-site-agentprovisioning. After placing a new order, provisioning is handled asynchronously bywaldur-k8s-site-agent. The agent creates GitLab subgroups and users. The role resolves the current pod for thewaldur-agent-cloud-order-processdeployment and scans that pod's logs for the returned order ID until processing completes. It searches for both compact and hyphenated UUID forms and treats the order as complete only when the matching logs containThe order <hyphenated-order-uuid> has been successfully processed. If the role reuses an existing complete order with a non-terminated resource, this polling step is skipped.The agent emits structured JSON log events. Example events:
-
Create and monitor the repository mirror Job. Render and apply
Job/themelio-seederusing direct container environment injection. Wait for the Job to complete by reading.status.succeededand.status.failedseparately. If it fails or times out, collect Job pod status and pod logs before cleanup. -
Clean up the Job. Delete
Job/themelio-seederand any remaining pods labeledjob-name=themelio-seedernear the end of the role after status and log collection. If the Job failed, fail the role after cleanup.
Job-controlled repository mirroring
-
Obtain a
pharos-adminimpersonation token. By this point,waldur-k8s-site-agenthas provisionedpharos-adminas a GitLab user. The Job calls GRA's existingPUT /users/:usernameendpoint to get the numeric GitLab user ID, then callsPOST /users/:user_id/impersonation_tokensto obtain a short-lived token withapi+read_repositoryscopes. This token is sufficient for authenticating git push operations over HTTPS.No new GRA endpoints are required. The existing
POST /users/:user_id/impersonation_tokensendpoint covers all repository write operations needed by the seeder. -
Enumerate repositories. Call the external GitLab API (
SEEDER_EXTERNAL_GITLAB_URL, defaulthttps://gitlab.grnet.gr) usingSEEDER_EXTERNAL_GITLAB_TOKENto list all repositories underSEEDER_EXTERNAL_GITLAB_GROUP_PATH(defaultthemelio/dev_gitlab_backup, read from the role defaults), recursively traversing subgroups. Keep only repositories whose full path containspharos.For example, skip:
Include:
-
Idempotency check. For each repository, query the internal GitLab API (using the
pharos-adminimpersonation token) to compare existing branch HEADs. If all remote branches are already present and up to date, skip the repository. -
Clone and push. For repositories that are missing or out of date:
- Clone the repository (including LFS objects) from the external GitLab into
/workspaceusingSEEDER_EXTERNAL_GITLAB_TOKEN. - Push all branches, tags, and LFS objects to the target path in the internal GitLab using the
pharos-adminimpersonation token. All seeded repositories are created as public. - Delete the local clone from
/workspacebefore moving to the next repository.
- Clone the repository (including LFS objects) from the external GitLab into
2. GitLab Root Actor Role
The gitlab-root-actor requires no modifications for the seeding workflow. The seeder uses the existing endpoint:
POST /users/:user_id/impersonation_tokens— creates a short-lived token forpharos-adminwithapi+read_repositoryscopes, which is sufficient to authenticate git push over HTTPS to the internal GitLab instance.
The seeder never holds the internal GitLab root token. It only holds:
- The external GitLab token (for cloning from the external instance)
- The short-lived
pharos-adminimpersonation token (for pushing to the internal instance)
3. Ansible Orchestration (seed-initial-admin-data role)
Seeding tasks are implemented in the dedicated seed-initial-admin-data Ansible role. The role is idempotent and can be run repeatedly.
New Ansible tasks
The role implements the following tasks:
New Ansible variables (defaults/main.yml)
External GitLab token
Before running the role, the operator creates a GRNET GitLab personal access token and exports it locally as SEEDER_EXTERNAL_GITLAB_TOKEN. Ansible reads that local environment variable, validates it against the external GitLab API, and caches the token in an Ansible fact for the current run. If the environment variable is missing, empty, or the validation request indicates the token is expired, the role terminates with a clear error before creating the Kubernetes Job.
The validated token is passed directly into the rendered K8s Job container environment as SEEDER_EXTERNAL_GITLAB_TOKEN. It is never committed to vault or read from a file. Because the rendered Job contains the token, all Ansible tasks that template, apply, or display the Job manifest must use no_log.
Vault variables
The role requires one vault variable:
Waldur operations are performed through kubectl exec into the Waldur application pod. The external GitLab token is supplied only through the operator's local SEEDER_EXTERNAL_GITLAB_TOKEN environment variable and cached as an Ansible fact for the current run.
New templates
Page source
Source snapshot · Revision f928834c8.