From c85d940c477798880cb34ffe8b4431db39eed664 Mon Sep 17 00:00:00 2001 From: Chalindu Kodikara Date: Fri, 16 Jan 2026 15:01:30 +0530 Subject: [PATCH 1/7] Add ci docs to operator manual --- docs/operations/ci/additional-resources.md | 123 ++++ .../ci/component-workflow-schema.md | 238 ++++++++ docs/operations/ci/custom-workflows.md | 563 ++++++++++++++++++ docs/operations/ci/overview.md | 421 +++++++++++++ 4 files changed, 1345 insertions(+) create mode 100644 docs/operations/ci/additional-resources.md create mode 100644 docs/operations/ci/component-workflow-schema.md create mode 100644 docs/operations/ci/custom-workflows.md create mode 100644 docs/operations/ci/overview.md diff --git a/docs/operations/ci/additional-resources.md b/docs/operations/ci/additional-resources.md new file mode 100644 index 0000000..7148b2d --- /dev/null +++ b/docs/operations/ci/additional-resources.md @@ -0,0 +1,123 @@ +--- +title: Additional Resources in Workflows +description: Learn how to define additional Kubernetes resources for ComponentWorkflows +sidebar_position: 4 +--- + +# Additional Resources in Workflows + +ComponentWorkflows can define additional Kubernetes resources that are needed for workflow execution. These resources are created in the Build Plane before the workflow runs and are automatically cleaned up when the workflow completes. + +## Overview + +When workflows need additional resources beyond the basic Argo Workflow definition—such as secrets for authentication, ConfigMaps for configuration, or ExternalSecrets for dynamic secret management—you can define them in the `resources` field of the ComponentWorkflow. + +### Common Use Cases + +- **ExternalSecrets**: Fetch secrets from external secret backends (AWS Secrets Manager, Vault, etc.) +- **ConfigMaps**: Provide configuration files or environment-specific settings +- **Secrets**: Store static credentials or tokens +- **Custom Resources**: Any Kubernetes resource needed by workflow steps + +## How It Works + +### Lifecycle + +1. **Creation**: When a ComponentWorkflowRun is created, the controller: + - Renders all resource templates by substituting template variables + - Creates the resources in the Build Plane namespace + +2. **Execution**: The Argo Workflow accesses these resources during execution. + +3. **Cleanup**: When the ComponentWorkflowRun is deleted: + - All resources defined in the `resources` field are automatically deleted + +## Defining Resources + +Resources are defined in the ComponentWorkflow's `spec.resources` field as a list of resource templates. + +### Basic Structure + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: ComponentWorkflow +metadata: + name: my-workflow + namespace: default +spec: + schema: + # ... schema definition + + resources: + - id: resource-identifier + template: + # Standard Kubernetes resource definition + apiVersion: v1 + kind: ConfigMap + metadata: + name: ${metadata.workflowRunName}-config + namespace: openchoreo-ci-${metadata.orgName} + data: + key: value + + runTemplate: + # ... Argo Workflow template +``` + +### Multiple Resources + +A workflow can define multiple resources: + +```yaml +resources: + # Git authentication + - id: git-secret + template: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: ${metadata.workflowRunName}-git-secret + namespace: openchoreo-ci-${metadata.orgName} + spec: + # ... ExternalSecret spec + + # Registry authentication + - id: registry-secret + template: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: ${metadata.workflowRunName}-registry-secret + namespace: openchoreo-ci-${metadata.orgName} + spec: + # ... ExternalSecret spec + + # Build configuration + - id: build-config + template: + apiVersion: v1 + kind: ConfigMap + metadata: + name: ${metadata.workflowRunName}-config + namespace: openchoreo-ci-${metadata.orgName} + data: + registry: "registry.example.com" + + # Custom webhook configuration + - id: webhook-config + template: + apiVersion: v1 + kind: ConfigMap + metadata: + name: ${metadata.workflowRunName}-webhook + namespace: openchoreo-ci-${metadata.orgName} + data: + notification.url: "https://slack.example.com/webhook" +``` + +## See Also + +- [CI Overview](./overview.md) - Understand ComponentWorkflows architecture +- [Custom Workflows](./custom-workflows.md) - Create custom ComponentWorkflows +- [Component Workflow Schema](./component-workflow-schema.mdx) - Parameter system and schema definition +- [External Secrets Operator](https://external-secrets.io/) - Secret management documentation diff --git a/docs/operations/ci/component-workflow-schema.md b/docs/operations/ci/component-workflow-schema.md new file mode 100644 index 0000000..3e18dbb --- /dev/null +++ b/docs/operations/ci/component-workflow-schema.md @@ -0,0 +1,238 @@ +--- +title: Component Workflow Schema +description: Learn how to define and use parameters in ComponentWorkflows +sidebar_position: 2 +--- + +# Component Workflow Schema + +ComponentWorkflows use a flexible parameter system that separates platform engineer governance from developer configuration. Parameters can be configured at both the platform engineer (PE) level and the developer level. + +## Parameter Categories + +### System Parameters + +System parameters have a **fixed structure** required for platform features like webhooks, UI integration, and build traceability. Platform engineers can customize defaults, enums, and descriptions, but must maintain the field structure. + +**Fixed structure:** + +```yaml +systemParameters: + repository: + url: string # Git repository URL + revision: + branch: string # Git branch to checkout + commit: string # Specific commit SHA (optional) + appPath: string # Path to application within repository +``` + +**Why this structure is fixed:** +- Enables webhooks to map Git events to components +- Powers UI actions like "build from latest commit" +- Provides build traceability and audit logs +- Supports monorepo workflows with `appPath` + +### Developer Parameters + +Developer parameters are **completely flexible** and defined by platform engineers based on the build strategy's requirements. These parameters appear in the UI when creating components and can include any build-specific configuration. + +**Common use cases:** +- Docker build context and Dockerfile path +- Build resources (CPU, memory) +- Buildpack configuration + +## Schema Definition + +The `schema` section in ComponentWorkflow defines both system and developer parameters using OpenChoreo's schema shorthand syntax. + +### Basic Parameter Schema + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: ComponentWorkflow +metadata: + name: docker + namespace: default +spec: + schema: + systemParameters: + repository: + url: string | description="Git repository URL" + revision: + branch: string | default=main description="Git branch to checkout" + commit: string | description="Git commit SHA or reference (optional)" + appPath: string | default=. description="Path to application directory" + + parameters: + docker: + context: string | default=. description="Docker build context path" + filePath: string | default=./Dockerfile description="Path to Dockerfile" +``` + +### Advanced Schema with Custom Types + +Platform engineers can define reusable types for complex parameter structures: + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: ComponentWorkflow +metadata: + name: google-cloud-buildpacks-advanced + namespace: default +spec: + schema: + # Define reusable types + types: + Endpoint: + name: string + port: integer + type: string | enum=REST,HTTP,TCP,UDP + schemaFile: string | description="Path to endpoint schema file" + + ResourceRequirements: + requests: ResourceQuantity | default={} + limits: ResourceQuantity | default={} + + ResourceQuantity: + cpu: string | default=100m + memory: string | default=256Mi + + systemParameters: + repository: + url: string | description="Git repository URL" + revision: + branch: string | default=main description="Git branch" + commit: string | description="Commit SHA (optional)" + appPath: string | default=. description="Application path" + + # Use custom types in parameters + parameters: + endpoints: '[]Endpoint | default=[] description="Service endpoints"' + resources: ResourceRequirements | default={} +``` + +## Template Variable Reference + +ComponentWorkflow templates support the following variable categories for use in the `runTemplate` field: + +| Variable Category | Syntax | Description | Example | +|-------------------|--------|-------------|---------| +| **Metadata** | `${metadata.*}` | System-provided workflow and component metadata | `${metadata.componentName}` | +| **System Parameters** | `${systemParameters.*}` | Repository and revision information | `${systemParameters.repository.url}` | +| **Developer Parameters** | `${parameters.*}` | Build-specific configuration from schema | `${parameters.docker.context}` | + +### Available Metadata Variables + +- `${metadata.workflowRunName}` - ComponentWorkflowRun CR name +- `${metadata.componentName}` - Component name +- `${metadata.projectName}` - Project name +- `${metadata.orgName}` - Organization name (namespace) + +## Using Parameters in runTemplate + +The `runTemplate` field defines the Argo Workflow that will be rendered and executed. Template variables are substituted with actual values from ComponentWorkflowRun. + +### Example: Docker Workflow + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: ComponentWorkflow +metadata: + name: docker + namespace: default +spec: + schema: + systemParameters: + repository: + url: string | description="Git repository URL" + revision: + branch: string | default=main description="Git branch" + commit: string | description="Commit SHA (optional)" + appPath: string | default=. description="Application path" + + parameters: + docker: + context: string | default=. description="Docker build context" + filePath: string | default=./Dockerfile description="Dockerfile path" + + # Template that will be rendered for each ComponentWorkflowRun + runTemplate: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + name: ${metadata.workflowRunName} + namespace: openchoreo-ci-${metadata.orgName} + spec: + arguments: + parameters: + # System parameters + - name: git-repo + value: ${systemParameters.repository.url} + - name: branch + value: ${systemParameters.repository.revision.branch} + - name: commit + value: ${systemParameters.repository.revision.commit} + - name: app-path + value: ${systemParameters.repository.appPath} + + # Developer parameters + - name: docker-context + value: ${parameters.docker.context} + - name: dockerfile-path + value: ${parameters.docker.filePath} + + # PE-controlled hardcoded parameters + - name: component-name + value: ${metadata.componentName} + - name: project-name + value: ${metadata.projectName} + - name: build-timeout + value: "30m" + - name: image-name + value: ${metadata.projectName}-${metadata.componentName}-image + - name: image-tag + value: v1 + + serviceAccountName: workflow-sa + workflowTemplateRef: + clusterScope: true + name: docker +``` + +## Parameter Flow + +Understanding how parameters flow through the system: + +``` +1. Platform Engineer defines ComponentWorkflow + └── Defines schema (systemParameters + parameters) + └── Defines runTemplate with ${...} variables + +2. Developer creates Component + └── References ComponentWorkflow by name + └── Provides values for systemParameters and parameters + +3. ComponentWorkflowRun created + └── Contains parameter values from Component + └── Controller renders runTemplate by substituting variables + +4. Argo Workflow executed in Build Plane + └── Receives resolved parameter values + └── Executes build steps with actual configuration +``` + +## Parameter Best Practices + +1. **Use System Parameters for Repository Info**: Always use the fixed `systemParameters.repository` structure for Git repository configuration +2. **Define Clear Schemas**: Provide descriptions and defaults for all developer parameters +3. **Use Enums for Limited Choices**: Constrain values using `enum` to prevent invalid configurations +4. **Create Reusable Types**: Define common structures as types for consistency across parameters +5. **Hardcode Governance Values**: Use PE-controlled hardcoded parameters for security policies, registry endpoints, and build timeouts + +## Validation and Defaults + +The ComponentWorkflowRun controller automatically: +- Validates parameter values against schema constraints +- Applies default values for parameters not provided by developers +- Converts complex values (arrays, objects) to appropriate formats for Argo Workflows +- Reports validation errors through ComponentWorkflowRun conditions diff --git a/docs/operations/ci/custom-workflows.md b/docs/operations/ci/custom-workflows.md new file mode 100644 index 0000000..90766a8 --- /dev/null +++ b/docs/operations/ci/custom-workflows.md @@ -0,0 +1,563 @@ +--- +title: Custom Component Workflows +description: Guide to create custom ComponentWorkflows and ClusterWorkflowTemplates +sidebar_position: 3 +--- + +# Custom ComponentWorkflows + +Platform engineers can create custom ComponentWorkflows to support specific build strategies, languages, or organizational requirements. This guide walks through creating a complete custom workflow from scratch. + +## Overview + +Creating a custom ComponentWorkflow involves two main steps: + +1. **Create ClusterWorkflowTemplate** in the build plane (defines the actual workflow steps) +2. **Create ComponentWorkflow** in the control plane (defines the schema and references the template) +3. Allow ComponentWorkflow in the Component Types + +## Step 1: Create ClusterWorkflowTemplate + +The ClusterWorkflowTemplate defines the actual Argo Workflow steps that will execute in the build plane. + +### Basic Structure + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ClusterWorkflowTemplate +metadata: + name: my-custom-workflow +spec: + entrypoint: main + volumeClaimTemplates: + - metadata: + name: work + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 2Gi + + templates: + - name: main + steps: + - - name: clone-step + template: clone + - - name: build-step + template: build + arguments: + parameters: + - name: git-revision + value: "{{steps.clone-step.outputs.parameters.git-revision}}" + - - name: push-step + template: push + arguments: + parameters: + - name: git-revision + value: "{{steps.clone-step.outputs.parameters.git-revision}}" + - - name: workload-create-step + template: workload-create + arguments: + parameters: + - name: image + value: "{{steps.push-step.outputs.parameters.image}}" + + # Define individual step templates below + - name: clone + # ... clone implementation + - name: build + # ... build implementation + - name: push + # ... push implementation + - name: workload-create + # ... workload create implementation +``` + +### Required Steps + +#### WorkloadCreate Step + +**Responsibilities:** +- Generate Workload CR using openchoreo-cli +- Include built image reference +- Merge workload.yaml from repository if present + +**inputs:** +```yaml +inputs: + parameters: + - name: image +``` + +**outputs:** +```yaml +outputs: + parameters: + - name: workload-cr + valueFrom: + path: /mnt/vol/workload-cr.yaml +``` + +:::important +The workload-create-step must output a parameter named `workload-cr`. The ComponentWorkflowRun controller expects this exact parameter name to retrieve the generated Workload CR. +::: + +### Example: Custom Google Cloud Buildpack Workflow Template + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ClusterWorkflowTemplate +metadata: + name: google-cloud-buildpacks +spec: + entrypoint: build-workflow + templates: + - name: build-workflow + steps: + - - name: clone-step + template: clone-step + - - arguments: + parameters: + - name: git-revision + value: '{{steps.clone-step.outputs.parameters.git-revision}}' + name: build-step + template: build-step + - - arguments: + parameters: + - name: git-revision + value: '{{steps.clone-step.outputs.parameters.git-revision}}' + name: push-step + template: push-step + - - arguments: + parameters: + - name: image + value: '{{steps.push-step.outputs.parameters.image}}' + name: workload-create-step + template: workload-create-step + - container: + args: + - |- + set -e + + ##################################################################### + # 1. Initialize variables + ##################################################################### + BRANCH={{workflow.parameters.branch}} + REPO_URL={{workflow.parameters.git-repo}} + COMMIT={{workflow.parameters.commit}} + + ##################################################################### + # 2. Read authentication token + ##################################################################### + TOKEN_FILE="/etc/secrets/git-secret/git-token" + GIT_TOKEN="" + if [ -f "$TOKEN_FILE" ]; then + GIT_TOKEN="$(cat "$TOKEN_FILE")" + fi + + ##################################################################### + # 3. Build authenticated repository URL + ##################################################################### + CLONE_URL="$REPO_URL" + if [ -n "$GIT_TOKEN" ]; then + case "$REPO_URL" in + https://*) + HOST=$(echo "$REPO_URL" | sed -E 's|https://([^/]+)/.*|\1|') + REPO_PATH=$(echo "$REPO_URL" | sed -E 's|https://[^/]+/(.*)|\1|') + ;; + git@*) + HOST=$(echo "$REPO_URL" | sed -E 's|git@([^:]+):.*|\1|') + REPO_PATH=$(echo "$REPO_URL" | sed -E 's|git@[^:]+:(.*)|\1|') + ;; + esac + + # Map host to authentication prefix + case "$HOST" in + github.com) AUTH_PREFIX="x-access-token" ;; + gitlab.com) AUTH_PREFIX="oauth2" ;; + bitbucket.org) AUTH_PREFIX="x-token-auth" ;; + *) AUTH_PREFIX="" ;; + esac + + if [ -n "$AUTH_PREFIX" ]; then + CLONE_URL="https://${AUTH_PREFIX}:${GIT_TOKEN}@${HOST}/${REPO_PATH}" + fi + fi + + echo "Cloning repository..." + + ##################################################################### + # 4. Clone repository + ##################################################################### + if [[ -n "$COMMIT" ]]; then + echo "Cloning specific commit: $COMMIT" + git clone --no-checkout --depth 1 "$CLONE_URL" /mnt/vol/source + cd /mnt/vol/source + git config --global advice.detachedHead false + git fetch --depth 1 origin "$COMMIT" + git checkout "$COMMIT" + echo -n "$COMMIT" | cut -c1-8 > /tmp/git-revision.txt + else + echo "Cloning branch: $BRANCH with latest commit" + git clone --single-branch --branch $BRANCH --depth 1 "$CLONE_URL" /mnt/vol/source + cd /mnt/vol/source + COMMIT_SHA=$(git rev-parse HEAD) + echo -n "$COMMIT_SHA" | cut -c1-8 > /tmp/git-revision.txt + fi + command: + - sh + - -c + image: alpine/git + name: "" + volumeMounts: + - mountPath: /mnt/vol + name: workspace + - mountPath: /etc/secrets/git-secret + name: git-secret + readOnly: true + name: clone-step + outputs: + parameters: + - name: git-revision + valueFrom: + path: /tmp/git-revision.txt + volumes: + - name: git-secret + secret: + optional: true + secretName: '{{workflow.parameters.git-secret}}' + - container: + args: + - |- + set -e + + WORKDIR=/mnt/vol/source + + IMAGE="{{workflow.parameters.image-name}}:{{workflow.parameters.image-tag}}-{{inputs.parameters.git-revision}}" + APP_PATH="{{workflow.parameters.app-path}}" + + ##################################################################### + # 1. Podman daemon + storage.conf + ##################################################################### + mkdir -p /etc/containers + cat > /etc/containers/storage.conf </dev/null | grep -q true; do sleep 1; done + + ##################################################################### + # 2. Registry configuration and pull pre-cached images + ##################################################################### + REGISTRY_ENDPOINT="host.k3d.internal:10082" + + # Pull pre-cached buildpack images from registry + BUILDER="${REGISTRY_ENDPOINT}/buildpacks-cache/google-builder:latest" + RUN_IMG="${REGISTRY_ENDPOINT}/buildpacks-cache/google-run:latest" + LIFECYCLE_IMG="${REGISTRY_ENDPOINT}/buildpacks-cache/lifecycle:0.20.5" + + echo "Pulling cached builder: $BUILDER" + podman pull --tls-verify=false "$BUILDER" + + echo "Pulling cached run image: $RUN_IMG" + podman pull --tls-verify=false "$RUN_IMG" + + echo "Pulling cached lifecycle: $LIFECYCLE_IMG" + podman pull --tls-verify=false "$LIFECYCLE_IMG" + + # Tag lifecycle image to expected name (referenced by builder image metadata) + podman tag "$LIFECYCLE_IMG" "docker.io/buildpacksio/lifecycle:0.20.5" + + ##################################################################### + # 3. Build with Google Buildpacks + ##################################################################### + /usr/local/bin/pack build "$IMAGE" \ + --builder "$BUILDER" \ + --run-image "$RUN_IMG" \ + --docker-host inherit \ + --path "$WORKDIR/$APP_PATH" \ + --pull-policy if-not-present + + podman save -o /mnt/vol/app-image.tar "$IMAGE" + command: + - sh + - -c + image: ghcr.io/openchoreo/podman-runner:v1.0 + securityContext: + privileged: true + volumeMounts: + - mountPath: /mnt/vol + name: workspace + inputs: + parameters: + - name: git-revision + name: build-step + - container: + args: + - |- + set -e + + ##################################################################### + # 1. Inputs + ##################################################################### + GIT_REVISION={{inputs.parameters.git-revision}} + IMAGE_NAME={{workflow.parameters.image-name}} + IMAGE_TAG={{workflow.parameters.image-tag}} + SRC_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}-${GIT_REVISION}" + + ##################################################################### + # 2. Registry + ##################################################################### + REGISTRY_ENDPOINT="host.k3d.internal:10082" + + ##################################################################### + # 3. Podman storage configuration + ##################################################################### + mkdir -p /etc/containers + cat < /etc/containers/storage.conf + [storage] + driver = "overlay" + runroot = "/run/containers/storage" + graphroot = "/var/lib/containers/storage" + [storage.options.overlay] + mount_program = "/usr/bin/fuse-overlayfs" + EOF + + ##################################################################### + # 4. Load the tarred image and push to registry + ##################################################################### + podman load -i /mnt/vol/app-image.tar + + podman tag $SRC_IMAGE $REGISTRY_ENDPOINT/$SRC_IMAGE + podman push --tls-verify=false $REGISTRY_ENDPOINT/$SRC_IMAGE + + ##################################################################### + # 5. Emit image reference (for later steps/kubelet pulls) + ##################################################################### + echo -n "$REGISTRY_ENDPOINT/$SRC_IMAGE" > /tmp/image.txt + command: + - sh + - -c + image: ghcr.io/openchoreo/podman-runner:v1.0 + securityContext: + privileged: true + volumeMounts: + - mountPath: /mnt/vol + name: workspace + inputs: + parameters: + - name: git-revision + name: push-step + outputs: + parameters: + - name: image + valueFrom: + path: /tmp/image.txt + - container: + args: + - |- + set -e + + ##################################################################### + # 1. Initialize variables + ##################################################################### + IMAGE={{inputs.parameters.image}} + PROJECT_NAME={{workflow.parameters.project-name}} + COMPONENT_NAME={{workflow.parameters.component-name}} + APP_PATH="{{workflow.parameters.app-path}}" + + DESCRIPTOR_PATH="/mnt/vol/source${APP_PATH:+/${APP_PATH#/}}" + + OUTPUT_PATH="/mnt/vol/workload-cr.yaml" + + echo "Creating workload with image: ${IMAGE}" + echo "Using descriptor in: ${DESCRIPTOR_PATH}" + + ##################################################################### + # 2. Podman storage configuration + ##################################################################### + mkdir -p /etc/containers + cat < /etc/containers/storage.conf + [storage] + driver = "overlay" + runroot = "/run/containers/storage" + graphroot = "/var/lib/containers/storage" + [storage.options.overlay] + mount_program = "/usr/bin/fuse-overlayfs" + EOF + + ##################################################################### + # 3. Create workload CR and export to output + ##################################################################### + # Check if workload.yaml exists and build the command accordingly + if [ -f "${DESCRIPTOR_PATH}/workload.yaml" ]; then + echo "Found workload.yaml descriptor, using it for workload creation" + podman run --rm --network=none \ + -v $DESCRIPTOR_PATH:/app:rw -w /app \ + ghcr.io/openchoreo/openchoreo-cli:latest-dev \ + create workload \ + --project "${PROJECT_NAME}" \ + --component "${COMPONENT_NAME}" \ + --image "${IMAGE}" \ + --descriptor "workload.yaml" \ + -o "workload-cr.yaml" + else + echo "No workload.yaml descriptor found, creating workload without descriptor" + podman run --rm --network=none \ + -v $DESCRIPTOR_PATH:/app:rw -w /app \ + ghcr.io/openchoreo/openchoreo-cli:latest-dev \ + create workload \ + --project "${PROJECT_NAME}" \ + --component "${COMPONENT_NAME}" \ + --image "${IMAGE}" \ + -o "workload-cr.yaml" + fi + + # Copy output CR to the shared volume + cp -f "${DESCRIPTOR_PATH}/workload-cr.yaml" "${OUTPUT_PATH}" + command: + - sh + - -c + image: ghcr.io/openchoreo/podman-runner:v1.0 + securityContext: + privileged: true + volumeMounts: + - mountPath: /mnt/vol + name: workspace + inputs: + parameters: + - name: image + name: workload-create-step + outputs: + parameters: + - name: workload-cr + valueFrom: + path: /mnt/vol/workload-cr.yaml + ttlStrategy: + secondsAfterCompletion: 3600 + volumeClaimTemplates: + - metadata: + creationTimestamp: null + name: workspace + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi +``` + +## Step 2: Create ComponentWorkflow + +The ComponentWorkflow defines the schema and references the ClusterWorkflowTemplate. + +### Example: Go ComponentWorkflow + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: ComponentWorkflow +metadata: + name: google-cloud-buildpacks + namespace: default +spec: + schema: + parameters: {} + systemParameters: + repository: + appPath: string | default=. description="Path to the application directory + within the repository" + revision: + branch: string | default=main description="Git branch to checkout" + commit: string | description="Git commit SHA or reference (optional, defaults + to latest)" + url: string | description="Git repository URL" + runTemplate: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + name: ${metadata.workflowRunName} + namespace: openchoreo-ci-${metadata.orgName} + spec: + arguments: + parameters: + - name: component-name + value: ${metadata.componentName} + - name: project-name + value: ${metadata.projectName} + - name: git-repo + value: ${systemParameters.repository.url} + - name: branch + value: ${systemParameters.repository.revision.branch} + - name: commit + value: ${systemParameters.repository.revision.commit} + - name: app-path + value: ${systemParameters.repository.appPath} + - name: image-name + value: ${metadata.projectName}-${metadata.componentName}-image + - name: image-tag + value: v1 + - name: git-secret + value: ${metadata.workflowRunName}-git-secret + serviceAccountName: workflow-sa + workflowTemplateRef: + clusterScope: true + name: google-cloud-buildpacks + resources: + - id: git-secret + template: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: ${metadata.workflowRunName}-git-secret + namespace: openchoreo-ci-${metadata.orgName} + spec: + data: + - remoteRef: + key: git-token + secretKey: git-token + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: default + target: + creationPolicy: Owner + name: ${metadata.workflowRunName}-git-secret +``` + +## Step 3: Allow ComponentWorkflow in ComponentType + +After creating the ClusterWorkflowTemplate and ComponentWorkflow, the final step is to make the workflow available to developers by adding it to the `allowedWorkflows` list in ComponentType CRs. + +### Why This Step is Required + +ComponentTypes act as governance boundaries that control which build strategies developers can use. The `allowedWorkflows` field explicitly lists which ComponentWorkflows are permitted for components of that type. + +### Update ComponentType CR + +Add the custom workflow name to the `allowedWorkflows` array in the relevant ComponentType CR: + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: ComponentType +metadata: + name: service + namespace: default +spec: + workloadType: deployment + allowedWorkflows: + - google-cloud-buildpacks # Existing workflow + - ballerina-buildpack # Existing workflow + - docker # Existing workflow + + # Schema and resources configuration + schema: + # ... component schema definition + resources: + # ... resource templates +``` diff --git a/docs/operations/ci/overview.md b/docs/operations/ci/overview.md new file mode 100644 index 0000000..7a05975 --- /dev/null +++ b/docs/operations/ci/overview.md @@ -0,0 +1,421 @@ +--- +title: Overview +description: Understand how CI works in OpenChoreo using ComponentWorkflows and Argo Workflows +sidebar_position: 1 +--- + +# CI with OpenChoreo + +OpenChoreo's CI capabilities enable platform engineers to define, manage, and execute build and automation workflows. By leveraging Kubernetes-native technologies like Argo Workflows, OpenChoreo provides a scalable and flexible CI solution that integrates seamlessly with existing DevOps toolchains. + +OpenChoreo currently supports only Argo Workflows as the underlying engine for executing CI workflows. It can be extended to support additional kubernetes-native engines in the future. + +## Core Concepts + +### ComponentWorkflow + +A **ComponentWorkflow** is a platform engineer-defined template that specifies *how* to build applications. It consists of: + +- **Schema**: Defines system parameters (fixed structure for repository, branch, commit) and developer parameters (flexible, PE-defined fields like version, build options, resources) +- **RunTemplate**: An Argo Workflow template with template variables (`${metadata.*}`, `${systemParameters.*}`, `${parameters.*}`) +- **Resources**: Additional Kubernetes resources needed for the workflow (e.g., ExternalSecrets for Git credentials) + +ComponentWorkflows live in the control plane and are referenced by Components. Platform engineers control which workflows are available and what parameters developers can configure. + +### ComponentWorkflowRun + +A **ComponentWorkflowRun** represents a single execution instance of a ComponentWorkflow. When created, it: + +- References the Component being built (projectName, componentName) +- References the ComponentWorkflow to use +- Provides actual values for system and developer parameters +- Tracks execution state through conditions (WorkflowPending, WorkflowRunning, WorkflowSucceeded, WorkflowFailed, WorkloadUpdated) +- Stores outputs (image reference, resource references, workflow run reference) + +ComponentWorkflowRuns are created either manually or automatically (e.g., via Git webhooks). + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Control Plane │ +│ │ +│ ┌──────────────────┐ ┌─────────────────────┐ │ +│ │ ComponentWorkflow│ │ComponentWorkflowRun │ │ +│ │ (Template) │◄────────│ (Execution) │ │ +│ └──────────────────┘ └──────────┬──────────┘ │ +│ │ │ │ +│ ▼ │ │ +│ References │ │ +│ ClusterWorkflowTemplate │ │ +│ │ │ +│ ▼ │ +│ ┌───────────────────────────────────────────────────┐ │ +│ │ ComponentWorkflowRun Controller │ │ +│ │ - Renders template with parameter values │ │ +│ │ - Creates namespace, RBAC, resources │ │ +│ │ - Applies Argo Workflow to Build Plane │ │ +│ │ - Polls workflow status │ │ +│ │ - Creates/updates Workload CR on success │ │ +│ └───────────────────────────┬───────────────────────┘ │ +└──────────────────────────────┼──────────────────────────────────┘ + │ Websocket connection + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ Build Plane │ +│ │ +│ ┌───────────────────┐ ┌──────────────────────────────────┐ │ +│ │ Argo Workflows │ │ ClusterWorkflowTemplate │ │ +│ │ Controller │─────▶│ │ │ +│ └───────────────────┘ │ ┌───────┐ ┌───────┐ ┌──────┐ │ │ +│ │ │ Clone │─▶│ Build │─▶│ Push │ │ │ +│ ┌───────────────────┐ │ └───────┘ └───────┘ └───┬──┘ │ │ +│ │ Resources │ │ │ │ │ +│ │ (ExternalSecrets, │ │ ┌──────────────────┐ │ │ │ +│ │ ConfigMaps, etc) │ │ │ WorkloadCreate │◄─────┘ │ │ +│ └───────────────────┘ │ └──────────────────┘ │ │ +│ └──────────────────────────────────┘ │ +│ │ +│ Executes workflow steps │ +│ Returns Workload CR │ +│ │ +└──────────────────────────────────────────────────────────────────────┘ +``` + +### Multi-Plane Separation + +- **Control Plane**: Hosts ComponentWorkflow and ComponentWorkflowRun CRs, orchestrates workflow execution +- **Build Plane**: Executes Argo Workflows using ClusterWorkflowTemplates, performs compute-intensive build operations +- **Communication**: Control plane controller connects to build plane via a websocket connection + +In Single Cluster Setup, both planes run in the same cluster. + +## Execution Lifecycle + +When a ComponentWorkflowRun is created, the following lifecycle occurs: + +1. **Initialization**: ComponentWorkflowRun CR created (manually or via webhook) +2. **Template Rendering**: Controller fetches ComponentWorkflow and renders the runTemplate by substituting all template variables with values from ComponentWorkflowRun +3. **Build Plane Setup**: Controller creates namespace (`openchoreo-ci-{orgName}`), ServiceAccount, Role, and RoleBinding in build plane +4. **Resource Application**: Additional resources (ExternalSecrets, ConfigMaps, etc.) applied to build plane +5. **Workflow Execution**: Rendered Argo Workflow applied to build plane, execution begins +6. **Status Polling**: Controller polls workflow status and updates ComponentWorkflowRun conditions: + - `WorkflowCompleted`: Workflow completed either successfully or with failure + - `WorkflowRunning`: Argo Workflow executing + - `WorkflowSucceeded`: Workflow completed successfully + - `WorkflowFailed`: Workflow failed or errored +7. **Workload Creation**: On success, controller extracts image reference from push-step output and creates/updates Workload CR +8. **Completion**: `WorkloadUpdated` condition set to true, reconciliation complete + +### Resource Cleanup + +When a ComponentWorkflowRun is deleted: +- Controller removes all resources created in build plane (ExternalSecrets, ConfigMaps, Workflow, etc.) + +## Default ComponentWorkflows + +OpenChoreo ships with four default ComponentWorkflows installed in the control plane: + +- **docker**: Builds container images using Docker/Podman with a Dockerfile +- **google-cloud-buildpacks**: Builds container images using Google Cloud Buildpacks (auto-detects language) +- **ballerina-buildpack**: Builds Ballerina applications using Ballerina-specific buildpacks +- **react**: Builds React applications with optimized frontend build process + +These workflows reference corresponding ClusterWorkflowTemplates in the build plane. + +## ClusterWorkflowTemplate + +ClusterWorkflowTemplates are an Argo Workflows concept used to define reusable workflow templates at cluster scope in the build plane. For more details, refer to the [Argo Workflows documentation](https://argo-workflows.readthedocs.io/en/latest/cluster-workflow-templates/). + +OpenChoreo ships four default ClusterWorkflowTemplates in the build plane: + +- **docker**: Docker-based builds using Dockerfile +- **google-cloud-buildpacks**: Google Cloud Buildpacks with automatic language detection +- **ballerina-buildpack**: Ballerina-specific buildpack builds +- **react**: React application builds + +Each template defines a standard four-step build workflow: + +## Build Workflow Steps + +### 1. Clone Step + +Clones the source repository (private or public) and supports both branch and specific commit checkout. + +**Key features:** +- Automatic Git provider detection (GitHub, GitLab, Bitbucket) +- Private repository authentication +- Specific commit checkout or latest commit on branch + +```yaml +##################################################################### +# 1. Initialize variables +##################################################################### +BRANCH={{workflow.parameters.branch}} +REPO_URL={{workflow.parameters.git-repo}} +COMMIT={{workflow.parameters.commit}} + +##################################################################### +# 2. Read authentication token +##################################################################### +TOKEN_FILE="/etc/secrets/git-secret/git-token" +GIT_TOKEN="" +if [ -f "$TOKEN_FILE" ]; then + GIT_TOKEN="$(cat "$TOKEN_FILE")" +fi + +##################################################################### +# 3. Build authenticated repository URL +##################################################################### +CLONE_URL="$REPO_URL" +if [ -n "$GIT_TOKEN" ]; then + case "$REPO_URL" in + https://*) + HOST=$(echo "$REPO_URL" | sed -E 's|https://([^/]+)/.*|\1|') + REPO_PATH=$(echo "$REPO_URL" | sed -E 's|https://[^/]+/(.*)|\1|') + ;; + git@*) + HOST=$(echo "$REPO_URL" | sed -E 's|git@([^:]+):.*|\1|') + REPO_PATH=$(echo "$REPO_URL" | sed -E 's|git@[^:]+:(.*)|\1|') + ;; + esac + + # Map host to authentication prefix + case "$HOST" in + github.com) AUTH_PREFIX="x-access-token" ;; + gitlab.com) AUTH_PREFIX="oauth2" ;; + bitbucket.org) AUTH_PREFIX="x-token-auth" ;; + *) AUTH_PREFIX="" ;; + esac + + if [ -n "$AUTH_PREFIX" ]; then + CLONE_URL="https://${AUTH_PREFIX}:${GIT_TOKEN}@${HOST}/${REPO_PATH}" + fi +fi + +echo "Cloning repository..." + +##################################################################### +# 4. Clone repository +##################################################################### +if [[ -n "$COMMIT" ]]; then + echo "Cloning specific commit: $COMMIT" + git clone --no-checkout --depth 1 "$CLONE_URL" /mnt/vol/source + cd /mnt/vol/source + git config --global advice.detachedHead false + git fetch --depth 1 origin "$COMMIT" + git checkout "$COMMIT" + echo -n "$COMMIT" | cut -c1-8 > /tmp/git-revision.txt +else + echo "Cloning branch: $BRANCH with latest commit" + git clone --single-branch --branch $BRANCH --depth 1 "$CLONE_URL" /mnt/vol/source + cd /mnt/vol/source + COMMIT_SHA=$(git rev-parse HEAD) + echo -n "$COMMIT_SHA" | cut -c1-8 > /tmp/git-revision.txt +fi +``` +### 2. Build Step + +The build step executes the actual container image build process. The specific commands vary based on the selected build strategy. + +**Example: Docker build step** + +```yaml +WORKDIR=/mnt/vol/source +IMAGE="{{workflow.parameters.image-name}}:{{workflow.parameters.image-tag}}-{{inputs.parameters.git-revision}}" +DOCKER_CONTEXT="{{workflow.parameters.docker-context}}" +DOCKERFILE_PATH="{{workflow.parameters.dockerfile-path}}" + +##################################################################### +# 1. Podman daemon + storage.conf +##################################################################### +mkdir -p /etc/containers +cat > /etc/containers/storage.conf < /etc/containers/storage.conf +[storage] +driver = "overlay" +runroot = "/run/containers/storage" +graphroot = "/var/lib/containers/storage" +[storage.options.overlay] +mount_program = "/usr/bin/fuse-overlayfs" +EOF + +##################################################################### +# 4. Load the tarred image and push to registry +##################################################################### +podman load -i /mnt/vol/app-image.tar + +podman tag $SRC_IMAGE $REGISTRY_ENDPOINT/$SRC_IMAGE +podman push --tls-verify=false $REGISTRY_ENDPOINT/$SRC_IMAGE + +##################################################################### +# 5. Emit image reference (for later steps/kubelet pulls) +##################################################################### +echo -n "$REGISTRY_ENDPOINT/$SRC_IMAGE" > /tmp/image.txt +``` + +### 4. WorkloadCreate Step + +The WorkloadCreate step generates a Workload CR (Custom Resource) that defines the runtime specification for the Component. A Workload includes container configurations, network endpoints, and connections to other services. + +**Process:** +1. Checks for `workload.yaml` descriptor in the repository at the specified `appPath` +2. Uses `openchoreo-cli` to create Workload CR +3. Outputs the Workload CR YAML + +**Behavior:** +- **With workload.yaml**: Full workload specification with endpoints, connections, and configurations +- **Without workload.yaml**: Basic workload with just the container image (additional configurations can be added at deployment time) + +The ComponentWorkflowRun controller retrieves this Workload CR from the workflow output and creates/updates the Workload resource in the control plane. + + +```yaml +##################################################################### +# 1. Initialize variables +##################################################################### +IMAGE={{inputs.parameters.image}} +PROJECT_NAME={{workflow.parameters.project-name}} +COMPONENT_NAME={{workflow.parameters.component-name}} +APP_PATH="{{workflow.parameters.app-path}}" + +DESCRIPTOR_PATH="/mnt/vol/source${APP_PATH:+/${APP_PATH#/}}" + +OUTPUT_PATH="/mnt/vol/workload-cr.yaml" + +echo "Creating workload with image: ${IMAGE}" +echo "Using descriptor in: ${DESCRIPTOR_PATH}" + +##################################################################### +# 2. Podman storage configuration +##################################################################### +mkdir -p /etc/containers +cat < /etc/containers/storage.conf +[storage] +driver = "overlay" +runroot = "/run/containers/storage" +graphroot = "/var/lib/containers/storage" +[storage.options.overlay] +mount_program = "/usr/bin/fuse-overlayfs" +EOF + +##################################################################### +# 3. Create workload CR and export to output +##################################################################### +# Check if workload.yaml exists and build the command accordingly +if [ -f "${DESCRIPTOR_PATH}/workload.yaml" ]; then + echo "Found workload.yaml descriptor, using it for workload creation" + podman run --rm --network=none \ + -v $DESCRIPTOR_PATH:/app:rw -w /app \ + ghcr.io/openchoreo/openchoreo-cli:latest-dev \ + create workload \ + --project "${PROJECT_NAME}" \ + --component "${COMPONENT_NAME}" \ + --image "${IMAGE}" \ + --descriptor "workload.yaml" \ + -o "workload-cr.yaml" +else + echo "No workload.yaml descriptor found, creating workload without descriptor" + podman run --rm --network=none \ + -v $DESCRIPTOR_PATH:/app:rw -w /app \ + ghcr.io/openchoreo/openchoreo-cli:latest-dev \ + create workload \ + --project "${PROJECT_NAME}" \ + --component "${COMPONENT_NAME}" \ + --image "${IMAGE}" \ + -o "workload-cr.yaml" +fi + +# Copy output CR to the shared volume +cp -f "${DESCRIPTOR_PATH}/workload-cr.yaml" "${OUTPUT_PATH}" +``` +:::important +The workload CR is an output of the workload-create-step with the parameter name `workload-cr`. This name must remain consistent even when creating custom ClusterWorkflowTemplates, as the ComponentWorkflowRun controller expects this specific output parameter to retrieve the generated Workload CR. + +```yaml +name: workload-create-step +outputs: + parameters: + - name: workload-cr + valueFrom: + path: /mnt/vol/workload-cr.yaml +``` +::: + +## Workflow Governance + +### Allowing ComponentWorkflows for Components + +Platform engineers control which ComponentWorkflows are available to developers by configuring the `allowedWorkflows` field in ComponentType CRs. + +**Example configuration:** + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: ComponentType +metadata: + name: service + namespace: default +spec: + workloadType: deployment + allowedWorkflows: + - google-cloud-buildpacks + - ballerina-buildpack + - docker + # ... other ComponentType spec fields +``` + +**Benefits:** +- **Governance**: Only approved build strategies can be used +- **Consistency**: Enforces organizational build standards +- **Compliance**: Ensures security scanning and build policies are applied +- **Flexibility**: Different component types can have different allowed workflows + +Components can only reference workflows from their component type's allowed list. This prevents developers from using unapproved or potentially insecure build processes. From fb5ab0c52e65b9d1ec098517a74faccfd8831c2b Mon Sep 17 00:00:00 2001 From: Chalindu Kodikara Date: Fri, 16 Jan 2026 15:01:42 +0530 Subject: [PATCH 2/7] Add private repo use cases --- docs/use-cases/private-repository-multiple.md | 215 ++++++++++++++++++ docs/use-cases/private-repository.mdx | 138 +++++++++++ sidebars.ts | 14 ++ 3 files changed, 367 insertions(+) create mode 100644 docs/use-cases/private-repository-multiple.md create mode 100644 docs/use-cases/private-repository.mdx diff --git a/docs/use-cases/private-repository-multiple.md b/docs/use-cases/private-repository-multiple.md new file mode 100644 index 0000000..e78e49c --- /dev/null +++ b/docs/use-cases/private-repository-multiple.md @@ -0,0 +1,215 @@ +--- +title: Multiple Git Organizations +description: Configure workflows to support multiple Git organizations, groups, or workspaces +sidebar_position: 2 +--- + +# Configuring Multiple Git Organizations + +When your organization works with repositories across multiple Git organizations, groups, or workspaces, you can configure ComponentWorkflows to let developers select which one to use for their builds. This eliminates the need to create separate workflows for each Git organization. + +## Use Cases + +- **Multiple GitHub Organizations**: Your company has separate GitHub organizations for different teams or projects +- **Multiple GitLab Groups**: Different GitLab groups for frontend, backend, and infrastructure repositories +- **Multiple Bitbucket Workspaces**: Separate Bitbucket workspaces for different business units +- **Mixed Providers**: Some projects in GitHub, others in GitLab or Bitbucket +- **Client Projects**: Building applications from different client organizations + +## How It Works + +Instead of hardcoding a single Git token: + +1. **Add a parameter** to the ComponentWorkflow schema that lets developers select the Git organization +2. **Store multiple tokens** in your secret backend, one for each organization/group/workspace +3. **Dynamically reference** the appropriate token based on the developer's selection + +## Configuration Steps + +### Step 1: Create Git Tokens for Each Organization + +Create a separate Personal Access Token (PAT) for each Git organization you want to support. + +### Step 2: Switch to Build Plane Context + +If your control plane and build plane are in **separate Kubernetes clusters**, switch to the build plane cluster context. This is where workflow execution and git cloning happens. + +```bash +kubectl config use-context +``` + +### Step 3: Store Tokens in Secret Backend + +Store each token in your secret backend using a naming convention like `-git-token`. + +**For Development/Testing (Fake Provider):** + +```bash +# Add token for organization A +kubectl patch clustersecretstore default --type='json' -p='[ + { + "op": "add", + "path": "/spec/provider/fake/data/-", + "value": { + "key": "github-acme-corp-git-token", + "value": "ghp_xxxxxxxxxxxx" + } + } +]' + +# Add token for organization B +kubectl patch clustersecretstore default --type='json' -p='[ + { + "op": "add", + "path": "/spec/provider/fake/data/-", + "value": { + "key": "github-acme-labs-git-token", + "value": "ghp_yyyyyyyyyyyy" + } + } +]' + +# Add token for organization C +kubectl patch clustersecretstore default --type='json' -p='[ + { + "op": "add", + "path": "/spec/provider/fake/data/-", + "value": { + "key": "github-open-source-git-token", + "value": "ghp_zzzzzzzzzzzz" + } + } +]' +``` + +**For Production (AWS Secrets Manager, Vault, etc.):** + +Store secrets with keys following the same pattern: +- `github-acme-corp-git-token` +- `github-acme-labs-git-token` +- `github-open-source-git-token` + +### Step 4: Configure ComponentWorkflow with Selection Parameter + +Add a parameter to your ComponentWorkflow schema that allows developers to select the Git organization: + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: ComponentWorkflow +metadata: + name: docker + namespace: default +spec: + schema: + systemParameters: + repository: + url: string | description="Git repository URL" + revision: + branch: string | default=main description="Git branch to checkout" + commit: string | description="Git commit SHA (optional)" + appPath: string | default=. description="Path to application directory" + + parameters: + # Parameter for selecting Git organization + gitOrganization: string | default=github-acme-corp enum=github-acme-corp,github-acme-labs,github-open-source description="Git organization/group/workspace to use for authentication" + docker: + context: string | default=. description="Docker build context path relative to the repository root" + filePath: string | default=./Dockerfile description="Path to the Dockerfile relative to the repository root" + + resources: + - id: git-secret + template: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: ${metadata.workflowRunName}-git-secret + namespace: openchoreo-ci-${metadata.orgName} + spec: + refreshInterval: 15s + secretStoreRef: + name: default + kind: ClusterSecretStore + target: + name: ${metadata.workflowRunName}-git-secret + creationPolicy: Owner + data: + # Dynamically fetch token based on selected organization + - secretKey: git-token + remoteRef: + key: ${parameters.gitOrganization}-git-token + + runTemplate: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + name: ${metadata.workflowRunName} + namespace: openchoreo-ci-${metadata.orgName} + spec: + arguments: + parameters: + - name: component-name + value: ${metadata.componentName} + - name: project-name + value: ${metadata.projectName} + - name: git-repo + value: ${systemParameters.repository.url} + - name: branch + value: ${systemParameters.repository.revision.branch} + - name: commit + value: ${systemParameters.repository.revision.commit} + - name: app-path + value: ${systemParameters.repository.appPath} + - name: image-name + value: ${metadata.projectName}-${metadata.componentName}-image + - name: image-tag + value: v1 + - name: git-secret + value: ${metadata.workflowRunName}-git-secret + serviceAccountName: workflow-sa + workflowTemplateRef: + clusterScope: true + name: docker +``` + +**Key Points:** + +1. **Parameter Definition**: `gitOrganization` parameter with enum constraining available options +2. **Default Value**: Set a sensible default (e.g., your primary organization) +3. **Dynamic Secret Key**: Use `${parameters.gitOrganization}-git-token` to fetch the correct token +4. **SecretKey**: Always use `git-token` as the secretKey (not the full key path) so the clone step works correctly + +### Step 5: Use in Components + +Developers can now select the Git organization when creating components: + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: Component +metadata: + name: my-service + namespace: default +spec: + owner: + projectName: my-project + componentType: deployment/service + + workflow: + name: docker + systemParameters: + repository: + url: "https://github.com/acme-labs/private-repo" # Repository in acme-labs org + revision: + branch: "main" + appPath: "/" + parameters: + gitOrganization: "github-acme-labs" # Select the corresponding organization + docker: + context: "/" + filePath: "/Dockerfile" +``` + +## See Also + +- [Private Repositories](./private-repository.mdx) - Basic private repository setup +- [Additional Resources](../operations/ci/additional-resources.md) - Working with ExternalSecrets and other resources +- [Component Workflow Schema](../operations/ci/component-workflow-schema.mdx) - Parameter system documentation diff --git a/docs/use-cases/private-repository.mdx b/docs/use-cases/private-repository.mdx new file mode 100644 index 0000000..2619884 --- /dev/null +++ b/docs/use-cases/private-repository.mdx @@ -0,0 +1,138 @@ +--- +title: Private Git Repository +description: Learn how to build applications from private Git repositories using ComponentWorkflows +sidebar_position: 1 +--- + +# Working with a Private Repository + +OpenChoreo's ComponentWorkflows support building from private Git repositories that require authentication. Private repository support is built-in for all default workflows and works seamlessly with GitHub, GitLab, and Bitbucket. + +Follow these steps to enable private repository access: + +### Step 1: Create a Git Personal Access Token + +Create a git token with read access to your repositories. + +### Step 2: Switch to Build Plane Context + +If your control plane and build plane are in **separate Kubernetes clusters**, switch to the build plane cluster context. This is where workflow execution and git cloning happens. + +```bash +kubectl config use-context +``` + +### Step 3: Store the Token in OpenChoreo + +**For Development/Testing (using fake provider):** + +```bash +kubectl patch clustersecretstore default --type='json' -p='[ + { + "op": "add", + "path": "/spec/provider/fake/data/-", + "value": { + "key": "git-token", + "value": "YourGitAccessToken" + } + } +]' +``` + +Verify the secret is configured: + +```bash +kubectl get clustersecretstore default -o yaml +``` + +**For Production (using real secret backend):** + +Configure a ClusterSecretStore for your secret manager (AWS Secrets Manager, HashiCorp Vault, etc.) and store your token with key `git-token`. + +### Step 4: Use Private Repository in Your Component + +Simply reference your private repository URL in the Component spec: + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: Component +metadata: + name: my-private-service + namespace: default +spec: + owner: + projectName: my-project + componentType: deployment/service + + workflow: + name: google-cloud-buildpacks + systemParameters: + repository: + url: "https://github.com/myorg/private-repo" # Your private repo + revision: + branch: "main" + appPath: "/" + parameters: {} +``` + +That's it! Trigger a build and the workflow will automatically handle authentication when cloning the repository. + +## How It Works + +Understanding the authentication flow helps with troubleshooting and customization. + +### Architecture Overview + +When building from a private repository: + +1. **ComponentWorkflow** defines an ExternalSecret resource that fetches your Git token from a secret backend +2. **ComponentWorkflowRun controller** creates the ExternalSecret in the Build Plane before executing the workflow +3. **Clone step** reads the token from the mounted secret, constructs an authenticated URL, and clones the repository +4. Credentials are never exposed in logs or workflow outputs + +This architecture separates secret management (control plane) from build execution (build plane) while maintaining security. + +### Clone Step + +The clone step automatically detects the Git provider and uses the appropriate authentication method: + +| Provider | Authentication Prefix | URL Format | +|----------|----------------------|------------| +| **GitHub** | `x-access-token` | `https://x-access-token:TOKEN@github.com/org/repo` | +| **GitLab** | `oauth2` | `https://oauth2:TOKEN@gitlab.com/org/repo` | +| **Bitbucket** | `x-token-auth` | `https://x-token-auth:TOKEN@bitbucket.org/org/repo` | + +### ExternalSecret Resource + +ComponentWorkflows define an ExternalSecret resource in the `resources` field: + +```yaml +resources: + - id: git-secret + template: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: ${metadata.workflowRunName}-git-secret + namespace: openchoreo-ci-${metadata.orgName} + spec: + refreshInterval: 15s + secretStoreRef: + name: default + kind: ClusterSecretStore + target: + name: ${metadata.workflowRunName}-git-secret + creationPolicy: Owner + data: + - secretKey: git-token + remoteRef: + key: git-token +``` + +The ComponentWorkflowRun controller creates this ExternalSecret in the Build Plane before executing the workflow, and the clone step mounts it as a volume. + +## See Also + +- [CI Overview](../operations/ci/overview.md) - Understand ComponentWorkflows and build architecture +- [Custom Workflows](../operations/ci/custom-workflows.mdx) - Create custom ComponentWorkflows +- [External Secrets Operator](https://external-secrets.io/) - Secret management documentation diff --git a/sidebars.ts b/sidebars.ts index b2f00d6..bc43215 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -59,6 +59,16 @@ const sidebars: SidebarsConfig = { 'operations/observability-alerting', 'operations/rca-agent', 'operations/upgrades', + { + type: 'category', + label: 'CI', + items: [ + 'operations/ci/overview', + 'operations/ci/component-workflow-schema', + 'operations/ci/custom-workflows', + 'operations/ci/additional-resources', + ], + }, { type: 'category', label: 'GitOps', @@ -81,6 +91,8 @@ const sidebars: SidebarsConfig = { items: [ 'use-cases/deploy-prebuilt-image', 'use-cases/api-management', + 'use-cases/private-repository', + 'use-cases/private-repository-multiple', ] }, { @@ -138,6 +150,7 @@ const sidebars: SidebarsConfig = { items: [ {type: 'doc', id: 'reference/api/application/project', label: 'Project'}, {type: 'doc', id: 'reference/api/application/component', label: 'Component'}, + {type: 'doc', id: 'reference/api/application/componentworkflowrun', label: 'ComponentWorkflowRun'}, {type: 'doc', id: 'reference/api/application/workload', label: 'Workload'}, {type: 'doc', id: 'reference/api/application/componentdeployment', label: 'ComponentDeployment (Deprecated)'}, {type: 'doc', id: 'reference/api/application/build', label: 'Build (Deprecated)'}, @@ -154,6 +167,7 @@ const sidebars: SidebarsConfig = { {type: 'doc', id: 'reference/api/platform/dataplane', label: 'DataPlane'}, {type: 'doc', id: 'reference/api/platform/environment', label: 'Environment'}, {type: 'doc', id: 'reference/api/platform/buildplane', label: 'BuildPlane'}, + {type: 'doc', id: 'reference/api/platform/componentworkflow', label: 'ComponentWorkflow'}, {type: 'doc', id: 'reference/api/platform/observabilityplane', label: 'ObservabilityPlane'}, {type: 'doc', id: 'reference/api/platform/observabilityalertrule', label: 'ObservabilityAlertRule'}, {type: 'doc', id: 'reference/api/platform/observabilityalertsnotificationchannel', label: 'ObservabilityAlertsNotificationChannel'}, From 77122b9d1b7aafc6a3f0de05214aa4f5c16a2b14 Mon Sep 17 00:00:00 2001 From: Chalindu Kodikara Date: Fri, 16 Jan 2026 15:13:58 +0530 Subject: [PATCH 3/7] Fix broken links --- docs/operations/ci/additional-resources.md | 2 +- docs/use-cases/private-repository-multiple.md | 2 +- docs/use-cases/private-repository.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/operations/ci/additional-resources.md b/docs/operations/ci/additional-resources.md index 7148b2d..6bbe7ab 100644 --- a/docs/operations/ci/additional-resources.md +++ b/docs/operations/ci/additional-resources.md @@ -119,5 +119,5 @@ resources: - [CI Overview](./overview.md) - Understand ComponentWorkflows architecture - [Custom Workflows](./custom-workflows.md) - Create custom ComponentWorkflows -- [Component Workflow Schema](./component-workflow-schema.mdx) - Parameter system and schema definition +- [Component Workflow Schema](./component-workflow-schema.md) - Parameter system and schema definition - [External Secrets Operator](https://external-secrets.io/) - Secret management documentation diff --git a/docs/use-cases/private-repository-multiple.md b/docs/use-cases/private-repository-multiple.md index e78e49c..e1c1c1f 100644 --- a/docs/use-cases/private-repository-multiple.md +++ b/docs/use-cases/private-repository-multiple.md @@ -212,4 +212,4 @@ spec: - [Private Repositories](./private-repository.mdx) - Basic private repository setup - [Additional Resources](../operations/ci/additional-resources.md) - Working with ExternalSecrets and other resources -- [Component Workflow Schema](../operations/ci/component-workflow-schema.mdx) - Parameter system documentation +- [Component Workflow Schema](../operations/ci/component-workflow-schema.md) - Parameter system documentation diff --git a/docs/use-cases/private-repository.mdx b/docs/use-cases/private-repository.mdx index 2619884..0f7ac48 100644 --- a/docs/use-cases/private-repository.mdx +++ b/docs/use-cases/private-repository.mdx @@ -134,5 +134,5 @@ The ComponentWorkflowRun controller creates this ExternalSecret in the Build Pla ## See Also - [CI Overview](../operations/ci/overview.md) - Understand ComponentWorkflows and build architecture -- [Custom Workflows](../operations/ci/custom-workflows.mdx) - Create custom ComponentWorkflows +- [Custom Workflows](../operations/ci/custom-workflows.md) - Create custom ComponentWorkflows - [External Secrets Operator](https://external-secrets.io/) - Secret management documentation From 15bfae05252110f260c6d1716b1f1ebeb1127a00 Mon Sep 17 00:00:00 2001 From: Chalindu Kodikara Date: Fri, 16 Jan 2026 17:49:00 +0530 Subject: [PATCH 4/7] Improve ci related docs --- docs/operations/ci/overview.md | 58 ++-------- docs/operations/ci/overview.png | Bin 0 -> 161488 bytes .../api/application/componentworkflowrun.md | 80 ++++++++++---- .../api/platform/componentworkflow.md | 103 +++++++++++++++++- docs/use-cases/private-repository-multiple.md | 8 +- docs/use-cases/private-repository.mdx | 2 +- 6 files changed, 176 insertions(+), 75 deletions(-) create mode 100644 docs/operations/ci/overview.png diff --git a/docs/operations/ci/overview.md b/docs/operations/ci/overview.md index 7a05975..3563bbf 100644 --- a/docs/operations/ci/overview.md +++ b/docs/operations/ci/overview.md @@ -8,7 +8,9 @@ sidebar_position: 1 OpenChoreo's CI capabilities enable platform engineers to define, manage, and execute build and automation workflows. By leveraging Kubernetes-native technologies like Argo Workflows, OpenChoreo provides a scalable and flexible CI solution that integrates seamlessly with existing DevOps toolchains. -OpenChoreo currently supports only Argo Workflows as the underlying engine for executing CI workflows. It can be extended to support additional kubernetes-native engines in the future. +:::note +OpenChoreo currently supports only Argo Workflows as the underlying engine for executing CI workflows. It can be extended to support more Kubernetes-native engines. +::: ## Core Concepts @@ -34,53 +36,17 @@ A **ComponentWorkflowRun** represents a single execution instance of a Component ComponentWorkflowRuns are created either manually or automatically (e.g., via Git webhooks). +:::warning Imperative Resource +ComponentWorkflowRun is an **imperative** resource—it triggers an action (a build) rather than declaring a desired state. Each time a ComponentWorkflowRun is applied, it initiates a new build execution. For this reason, do not include ComponentWorkflowRuns in GitOps repositories. Instead, create them through Git webhooks, the UI, or direct `kubectl apply` commands. +::: + ## Architecture -``` -┌─────────────────────────────────────────────────────────────────┐ -│ Control Plane │ -│ │ -│ ┌──────────────────┐ ┌─────────────────────┐ │ -│ │ ComponentWorkflow│ │ComponentWorkflowRun │ │ -│ │ (Template) │◄────────│ (Execution) │ │ -│ └──────────────────┘ └──────────┬──────────┘ │ -│ │ │ │ -│ ▼ │ │ -│ References │ │ -│ ClusterWorkflowTemplate │ │ -│ │ │ -│ ▼ │ -│ ┌───────────────────────────────────────────────────┐ │ -│ │ ComponentWorkflowRun Controller │ │ -│ │ - Renders template with parameter values │ │ -│ │ - Creates namespace, RBAC, resources │ │ -│ │ - Applies Argo Workflow to Build Plane │ │ -│ │ - Polls workflow status │ │ -│ │ - Creates/updates Workload CR on success │ │ -│ └───────────────────────────┬───────────────────────┘ │ -└──────────────────────────────┼──────────────────────────────────┘ - │ Websocket connection - ▼ -┌──────────────────────────────────────────────────────────────────────┐ -│ Build Plane │ -│ │ -│ ┌───────────────────┐ ┌──────────────────────────────────┐ │ -│ │ Argo Workflows │ │ ClusterWorkflowTemplate │ │ -│ │ Controller │─────▶│ │ │ -│ └───────────────────┘ │ ┌───────┐ ┌───────┐ ┌──────┐ │ │ -│ │ │ Clone │─▶│ Build │─▶│ Push │ │ │ -│ ┌───────────────────┐ │ └───────┘ └───────┘ └───┬──┘ │ │ -│ │ Resources │ │ │ │ │ -│ │ (ExternalSecrets, │ │ ┌──────────────────┐ │ │ │ -│ │ ConfigMaps, etc) │ │ │ WorkloadCreate │◄─────┘ │ │ -│ └───────────────────┘ │ └──────────────────┘ │ │ -│ └──────────────────────────────────┘ │ -│ │ -│ Executes workflow steps │ -│ Returns Workload CR │ -│ │ -└──────────────────────────────────────────────────────────────────────┘ -``` + ### Multi-Plane Separation diff --git a/docs/operations/ci/overview.png b/docs/operations/ci/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..28c8bd5bb22c7d9e30bef1c6005905deb114a4f8 GIT binary patch literal 161488 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

PO?xN`2bwj-y}s@i7GGj<*Bq}BE= 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%0D=cqaTi8QzctgP3h!!+9 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 literal 0 HcmV?d00001 diff --git a/docs/reference/api/application/componentworkflowrun.md b/docs/reference/api/application/componentworkflowrun.md index 7aedc30..5a9ee8a 100644 --- a/docs/reference/api/application/componentworkflowrun.md +++ b/docs/reference/api/application/componentworkflowrun.md @@ -77,11 +77,12 @@ referenced ComponentWorkflow. These values are validated against the ComponentWo ### Status Fields -| Field | Type | Default | Description | -|------------------|-------------------------------------------------------------------|---------|-------------------------------------------------------------| -| `conditions` | []Condition | [] | Standard Kubernetes conditions tracking execution state | -| `imageStatus` | [ComponentWorkflowImage](#componentworkflowimage) | - | Information about the built container image | -| `runReference` | [ComponentWorkflowRunReference](#componentworkflowrunreference) | - | Reference to the workflow execution resource in build plane | +| Field | Type | Default | Description | +|------------------|---------------------------------------------------|---------|-------------------------------------------------------------| +| `conditions` | []Condition | [] | Standard Kubernetes conditions tracking execution state | +| `imageStatus` | [ComponentWorkflowImage](#componentworkflowimage) | - | Information about the built container image | +| `runReference` | [ResourceReference](#resourcereference) | - | Reference to the workflow execution resource in build plane | +| `resources` | [][ResourceReference](#resourcereference) | - | References to additional resources created in build plane (for cleanup) | #### ComponentWorkflowImage @@ -89,20 +90,35 @@ referenced ComponentWorkflow. These values are validated against the ComponentWo |---------|--------|---------|-----------------------------------------------------------------------| | `image` | string | "" | Fully qualified image name (e.g., registry.example.com/myapp:v1.0.0) | -#### ComponentWorkflowRunReference +#### ResourceReference -| Field | Type | Default | Description | -|-------------|--------|---------|-----------------------------------------------------------------| -| `name` | string | "" | Name of the workflow run resource in the build plane cluster | -| `namespace` | string | "" | Namespace of the workflow run resource in the build plane cluster | +| Field | Type | Default | Description | +|--------------|--------|---------|-----------------------------------------------------------------| +| `apiVersion` | string | "" | API version of the resource (e.g., "v1", "apps/v1") | +| `kind` | string | "" | Kind of the resource (e.g., "Secret", "ConfigMap", "Workflow") | +| `name` | string | "" | Name of the resource in the build plane cluster | +| `namespace` | string | "" | Namespace of the resource in the build plane cluster | #### Condition Types -Common condition types for ComponentWorkflowRun resources: +ComponentWorkflowRun resources use the following condition types to track execution state: -- `Ready` - Indicates if the workflow run has completed successfully -- `Running` - Indicates if the workflow is currently executing -- `Failed` - Indicates if the workflow execution failed +- `WorkflowCompleted` - Indicates if the workflow has completed (successfully or with failure) +- `WorkflowRunning` - Indicates if the workflow is currently executing in the build plane +- `WorkflowSucceeded` - Indicates if the workflow execution completed successfully +- `WorkflowFailed` - Indicates if the workflow execution failed or errored +- `WorkloadUpdated` - Indicates if the Workload CR was successfully created/updated after workflow success + +#### Condition Reasons + +Common reasons used in ComponentWorkflowRun conditions: + +- `WorkflowPending` - Workflow has not been initiated yet +- `WorkflowRunning` - Workflow is currently executing +- `WorkflowSucceeded` - Workflow completed successfully +- `WorkflowFailed` - Workflow execution failed +- `WorkloadUpdated` - Workload CR successfully created/updated +- `WorkloadUpdateFailed` - Failed to create/update Workload CR ## Examples @@ -237,21 +253,47 @@ spec: ## Status Example -After execution, a ComponentWorkflowRun status might look like: +After successful execution, a ComponentWorkflowRun status might look like: ```yaml status: conditions: - - type: Ready + - type: WorkflowCompleted status: "True" lastTransitionTime: "2024-01-15T10:30:00Z" reason: WorkflowSucceeded - message: Workflow execution completed successfully + message: Workflow has completed successfully + observedGeneration: 1 + - type: WorkflowRunning + status: "False" + lastTransitionTime: "2024-01-15T10:29:30Z" + reason: WorkflowRunning + message: Argo Workflow running has completed + observedGeneration: 1 + - type: WorkflowSucceeded + status: "True" + lastTransitionTime: "2024-01-15T10:30:00Z" + reason: WorkflowSucceeded + message: Workflow completed successfully + observedGeneration: 1 + - type: WorkloadUpdated + status: "True" + lastTransitionTime: "2024-01-15T10:30:15Z" + reason: WorkloadUpdated + message: Workload CR created/updated successfully + observedGeneration: 1 imageStatus: - image: gcr.io/openchoreo-dev/images/default-reading-list-service-image:v1 + image: gcr.io/openchoreo-dev/images/default-reading-list-service-image:v1-a1b2c3d4 runReference: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow name: reading-list-service-build-01 namespace: openchoreo-ci-default + resources: + - apiVersion: external-secrets.io/v1 + kind: ExternalSecret + name: reading-list-service-build-01-git-secret + namespace: openchoreo-ci-default ``` ## Build-Specific Platform Features @@ -291,4 +333,4 @@ ComponentWorkflowRuns support the following annotations: - [ComponentWorkflow](../platform/componentworkflow.md) - Template definitions for component workflow execution - [Component](./component.md) - Components that reference ComponentWorkflows -- [ComponentType](../platform/componenttype.md) - Can restrict allowed component workflows \ No newline at end of file +- [ComponentType](../platform/componenttype.md) - Can restrict allowed component workflows diff --git a/docs/reference/api/platform/componentworkflow.md b/docs/reference/api/platform/componentworkflow.md index 92c2857..6b264e7 100644 --- a/docs/reference/api/platform/componentworkflow.md +++ b/docs/reference/api/platform/componentworkflow.md @@ -29,14 +29,21 @@ metadata: ### Spec Fields -| Field | Type | Required | Default | Description | -|---------------|--------|----------|---------|----------------------------------------------------------------------------------------------| -| `schema` | object | Yes | - | Parameter schemas including required system parameters and flexible developer parameters | -| `runTemplate` | object | Yes | - | Kubernetes resource template (typically Argo Workflow) with CEL expressions for runtime evaluation | +| Field | Type | Required | Default | Description | +|---------------|---------------------------------------------------------|----------|---------|----------------------------------------------------------------------------------------------| +| `schema` | [ComponentWorkflowSchema](#schema) | Yes | - | Parameter schemas including required system parameters and flexible developer parameters | +| `runTemplate` | object | Yes | - | Kubernetes resource template (typically Argo Workflow) with CEL expressions for runtime evaluation | +| `resources` | [][ComponentWorkflowResource](#componentworkflowresource) | No | - | Additional Kubernetes resources to be created alongside the workflow (e.g., ExternalSecrets, ConfigMaps) | ### Schema -The schema field defines two distinct parameter sections: +The schema field defines parameter schemas for the ComponentWorkflow: + +| Field | Type | Required | Default | Description | +|--------------------|--------|----------|---------|-----------------------------------------------------------------------| +| `types` | object | No | - | Reusable type definitions that can be referenced in schema fields | +| `systemParameters` | object | Yes | - | Required structured schema for repository information | +| `parameters` | object | No | - | Flexible PE-defined schema for additional build configuration | #### System Parameters Schema (Required) @@ -88,6 +95,27 @@ schema: paths: '[]string | default=["/root/.cache"]' ``` +#### Types (Optional Reusable Type Definitions) + +The optional `types` field allows platform engineers to define reusable type definitions that can be referenced in the parameter schema, similar to ComponentType: + +```yaml +schema: + types: + Endpoint: + name: string + port: integer + type: string | enum=REST,HTTP,TCP,UDP + ResourceLimit: + cpu: string | default=1000m + memory: string | default=1Gi + + parameters: + endpoints: '[]Endpoint | default=[]' + limits: ResourceLimit + buildArgs: '[]string | default=[]' +``` + ## CEL Variables in Run Templates ComponentWorkflow run templates support CEL expressions with access to: @@ -101,6 +129,49 @@ ComponentWorkflow run templates support CEL expressions with access to: | `${systemParameters.*}` | Repository information from system parameters | | `${parameters.*}` | Developer-provided values from the flexible parameter schema | +### ComponentWorkflowResource + +Additional Kubernetes resources that should be created alongside the workflow execution. These resources are typically used for secrets, configuration, or other supporting resources needed during the build. + +| Field | Type | Required | Default | Description | +|------------|--------|----------|---------|----------------------------------------------------------------------------------| +| `id` | string | Yes | - | Unique identifier for this resource within the ComponentWorkflow (min length: 1) | +| `template` | object | Yes | - | Kubernetes resource template with CEL expressions (same variables as runTemplate) | + +**Common Use Cases:** +- **ExternalSecrets**: Fetch git tokens or credentials from secret stores +- **ConfigMaps**: Provide build-time configuration +- **Secrets**: Store sensitive data needed during build + +**Resource Lifecycle:** +- Resources are created in the build plane before the workflow execution begins +- Resource references are tracked in the ComponentWorkflowRun status for cleanup +- When a ComponentWorkflowRun is deleted, the controller automatically cleans up all associated resources + +**Example:** +```yaml +resources: + - id: git-secret + template: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: ${metadata.workflowRunName}-git-secret + namespace: openchoreo-ci-${metadata.orgName} + spec: + refreshInterval: 15s + secretStoreRef: + name: default + kind: ClusterSecretStore + target: + name: ${metadata.workflowRunName}-git-secret + creationPolicy: Owner + data: + - secretKey: git-token + remoteRef: + key: git-token +``` + ## Examples ### Google Cloud Buildpacks ComponentWorkflow @@ -249,6 +320,28 @@ spec: workflowTemplateRef: clusterScope: true name: docker + + # Additional resources needed for the workflow + resources: + - id: git-secret + template: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: ${metadata.workflowRunName}-git-secret + namespace: openchoreo-ci-${metadata.orgName} + spec: + refreshInterval: 15s + secretStoreRef: + name: default + kind: ClusterSecretStore + target: + name: ${metadata.workflowRunName}-git-secret + creationPolicy: Owner + data: + - secretKey: git-token + remoteRef: + key: git-token ``` ## ComponentType Integration diff --git a/docs/use-cases/private-repository-multiple.md b/docs/use-cases/private-repository-multiple.md index e1c1c1f..bbe36c8 100644 --- a/docs/use-cases/private-repository-multiple.md +++ b/docs/use-cases/private-repository-multiple.md @@ -1,12 +1,12 @@ --- -title: Multiple Git Organizations -description: Configure workflows to support multiple Git organizations, groups, or workspaces +title: Multiple Private Git Organizations +description: Configure workflows to authenticate with private repositories across multiple Git organizations, groups, or workspaces sidebar_position: 2 --- -# Configuring Multiple Git Organizations +# Configure Multiple Private Git Organizations -When your organization works with repositories across multiple Git organizations, groups, or workspaces, you can configure ComponentWorkflows to let developers select which one to use for their builds. This eliminates the need to create separate workflows for each Git organization. +When your organization works with private repositories across multiple Git organizations, groups, or workspaces, you can configure ComponentWorkflows to let developers select which credentials to use for their builds. This eliminates the need to create separate workflows for each Git organization. ## Use Cases diff --git a/docs/use-cases/private-repository.mdx b/docs/use-cases/private-repository.mdx index 0f7ac48..9c96aae 100644 --- a/docs/use-cases/private-repository.mdx +++ b/docs/use-cases/private-repository.mdx @@ -4,7 +4,7 @@ description: Learn how to build applications from private Git repositories using sidebar_position: 1 --- -# Working with a Private Repository +# Configure a Private Git Repository OpenChoreo's ComponentWorkflows support building from private Git repositories that require authentication. Private repository support is built-in for all default workflows and works seamlessly with GitHub, GitLab, and Bitbucket. From 86c7895317b7a0542e0405262b46d835bf55e634 Mon Sep 17 00:00:00 2001 From: Chalindu Kodikara Date: Fri, 16 Jan 2026 17:49:55 +0530 Subject: [PATCH 5/7] Add external container registry configuration --- .../operations/component-workflow-secrets.mdx | 329 ------------------ docs/operations/container-registry.mdx | 296 ---------------- .../use-cases/external-container-registry.mdx | 325 +++++++++++++++++ 3 files changed, 325 insertions(+), 625 deletions(-) delete mode 100644 docs/operations/component-workflow-secrets.mdx delete mode 100644 docs/operations/container-registry.mdx create mode 100644 docs/use-cases/external-container-registry.mdx diff --git a/docs/operations/component-workflow-secrets.mdx b/docs/operations/component-workflow-secrets.mdx deleted file mode 100644 index 0bbe04d..0000000 --- a/docs/operations/component-workflow-secrets.mdx +++ /dev/null @@ -1,329 +0,0 @@ ---- -title: Component Workflow Secrets -description: Configure secrets for ComponentWorkflows to access private repositories and registries. -sidebar_position: 9 ---- - -# Component Workflow Secrets - -ComponentWorkflows often need access to secrets for authentication during the build process. This guide explains how to provide secrets to the Build Plane for common scenarios like cloning from private repositories and pushing to private registries. - -## Overview - -Build workflows require credentials for: -- **Git authentication** - Cloning source code from private repositories (GitHub, GitLab, Bitbucket) -- **Registry authentication** - Pushing container images to private registries (Docker Hub, GCR, ECR, Azure ACR) -- **External services** - Accessing APIs or services during the build process - -OpenChoreo supports two approaches for providing secrets to the Build Plane: - -1. **External Secrets Operator (Recommended for GitOps)** - Automatically sync secrets from external secret managers -2. **Manual Secret Creation** - Create Kubernetes secrets directly in the Build Plane - -## Secret Management Approaches - -### Option 1: External Secrets Operator (Recommended) - -Use the `resources` field in ComponentWorkflow to define ExternalSecret resources that automatically sync secrets from external secret managers like AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, and more. - -**Benefits:** -- Secrets are automatically synced -- No manual secret management required -- Ideal for GitOps workflows where all configuration is version-controlled -- Secrets never appear in Git repositories - -**Example:** - -```yaml -apiVersion: openchoreo.dev/v1alpha1 -kind: ComponentWorkflow -metadata: - name: my-workflow -spec: - # ... schema and runTemplate ... - - resources: - - id: registry-credentials - template: - apiVersion: external-secrets.io/v1 - kind: ExternalSecret - metadata: - name: registry-push-secret - namespace: openchoreo-ci-${metadata.orgName} - spec: - refreshInterval: 15s - secretStoreRef: - name: default - kind: ClusterSecretStore - target: - name: registry-push-secret - creationPolicy: Owner - data: - - secretKey: dockerconfig - remoteRef: - key: my-secret-key - property: dockerconfigjson -``` - -The ExternalSecret resource is created in the build execution namespace (e.g., `openchoreo-ci-default`) and automatically syncs credentials from your configured secret store. - -### Option 2: Manual Secret Creation - -Create Kubernetes secrets directly in the Build Plane's execution namespace. This is simpler for development and testing environments. - -**Benefits:** -- Simple setup for development and testing -- No additional operators required -- Direct control over secret lifecycle - -Secrets are created in the build execution namespace, which follows the pattern `openchoreo-ci-{organization-name}`. - -## Private Git Repositories - -When building from private repositories, the clone step needs authentication credentials. - -### External Secrets Setup - -1. Store your Git access token in your secrets manager - -2. Add the ExternalSecret resource to your ComponentWorkflow: - -```yaml -apiVersion: openchoreo.dev/v1alpha1 -kind: ComponentWorkflow -metadata: - name: my-workflow -spec: - resources: - - id: git-credentials - template: - apiVersion: external-secrets.io/v1 - kind: ExternalSecret - metadata: - name: git-clone-secret - namespace: openchoreo-ci-${metadata.orgName} - spec: - refreshInterval: 15s - secretStoreRef: - name: default - kind: ClusterSecretStore - target: - name: git-clone-secret - creationPolicy: Owner - data: - - secretKey: clone-secret - remoteRef: - key: github-pat - property: token -``` - -### Manual Setup - -1. Generate a personal access token from your Git provider. - -2. Create the secret: - -```bash -kubectl create secret generic git-clone-secret \ - --from-literal=clone-secret= \ - -n openchoreo-ci-default -``` - -Or apply a YAML manifest: - -```yaml -apiVersion: v1 -kind: Secret -metadata: - name: git-clone-secret - namespace: openchoreo-ci-default -type: Opaque -stringData: - clone-secret: '' -``` - -## Private Container Registries - -When pushing built images to private registries, the push step needs registry credentials. - - -### External Secrets Setup - -1. Store your Docker config JSON in your secrets manager - -2. Add the ExternalSecret resource to your ComponentWorkflow: - -```yaml -apiVersion: openchoreo.dev/v1alpha1 -kind: ComponentWorkflow -metadata: - name: my-workflow -spec: - resources: - - id: registry-credentials - template: - apiVersion: external-secrets.io/v1 - kind: ExternalSecret - metadata: - name: registry-push-secret - namespace: openchoreo-ci-${metadata.orgName} - spec: - refreshInterval: 15s - secretStoreRef: - name: default - kind: ClusterSecretStore - target: - name: registry-push-secret - creationPolicy: Owner - data: - - secretKey: .dockerconfigjson - remoteRef: - key: registry-credentials - property: dockerconfigjson -``` - -### Manual Setup - -1. Generate credentials for your registry. - -2. Create the docker-registry secret: - -```bash -kubectl create secret docker-registry registry-push-secret \ - --docker-server=https://index.docker.io/v1/ \ - --docker-username=your-username \ - --docker-password=your-password \ - -n openchoreo-ci-default -``` - -For multiple registries or custom docker config: - -```bash -# Create docker-config.json -cat > docker-config.json < \ - --docker-username= \ - --docker-password= \ - -n openchoreo-data-plane -``` - -2. Add the `imagePullSecrets` field to your ComponentType's Deployment template: - -```yaml -apiVersion: openchoreo.dev/v1alpha1 -kind: ComponentType -metadata: - name: my-service-type -spec: - resources: - - id: deployment - template: - apiVersion: apps/v1 - kind: Deployment - metadata: - name: ${metadata.name} - namespace: ${metadata.namespace} - spec: - template: - spec: - # Add imagePullSecrets here - imagePullSecrets: - - name: registry-pull-secret - containers: - - name: main - image: ${workload.containers[parameters.containerName].image} - # ... container configuration -``` - -## Next Steps - -- [Secret Management](./secret-management.mdx): Configure External Secrets Operator -- [Container Registry Configuration](./container-registry.mdx): Configure registry endpoints -- [ComponentWorkflow Samples](https://github.com/openchoreo/openchoreo/tree/main/samples/component-workflows): Complete working examples diff --git a/docs/operations/container-registry.mdx b/docs/operations/container-registry.mdx deleted file mode 100644 index d74d7c1..0000000 --- a/docs/operations/container-registry.mdx +++ /dev/null @@ -1,296 +0,0 @@ ---- -title: Container Registry Configuration -description: Configure the Build Plane to push images to your container registry and Data Planes to pull them. -sidebar_position: 7 ---- - -# Container Registry Configuration - -This guide explains how the Build Plane executes CI/CD workflows, how to configure registry credentials, and how Data Planes pull images for deployment. - -## Build Plane Overview - -The Build Plane provides CI/CD infrastructure using [Argo Workflows](https://argoproj.github.io/workflows/). When you trigger a build, OpenChoreo creates a Workflow that executes a series of steps to build your application, push the container image, and generate deployment artifacts. - -``` -┌─────────────────────────────────────────────────────────────────────────┐ -│ Build Plane │ -│ │ -│ ┌──────────────────┐ ┌──────────────────────────────────────┐ │ -│ │ Argo Workflows │ │ ClusterWorkflowTemplate │ │ -│ │ Controller │─────▶│ │ │ -│ └──────────────────┘ │ ┌───────┐ ┌───────┐ ┌────────────┐ │ │ -│ │ │ Clone │→│ Build │→│ Push │ │ │ -│ │ └───────┘ └───────┘ └────────────┘ │ │ -│ │ ↓ │ │ -│ │ ┌────────────┐ │ │ -│ │ │ Workload │ │ │ -│ │ │ Create │ │ │ -│ │ └────────────┘ │ │ -│ └──────────────────────────────────────┘ │ -│ │ │ -└───────────────────────────────────────────┼─────────────────────────────┘ - │ - ▼ - Container Registry - │ - ▼ - Data Plane -``` - -The Argo Workflows controller watches for Workflow resources and executes them. Each workflow follows a `ClusterWorkflowTemplate` that defines the build pipeline steps. - -## Registry Configuration - -Before workflows can push images, you need to configure registry credentials. Create a docker registry secret in the `openchoreo-build-plane` namespace: - -```bash -kubectl create secret docker-registry registry-credentials \ - --docker-server= \ - --docker-username= \ - --docker-password= \ - -n openchoreo-build-plane -``` - -For provider-specific credential setup, refer to: - -- [Amazon ECR](https://docs.aws.amazon.com/AmazonECR/latest/userguide/registry_auth.html) -- [Google Artifact Registry](https://cloud.google.com/artifact-registry/docs/docker/authentication) -- [Azure Container Registry](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-authentication) -- [Harbor](https://goharbor.io/docs/2.0.0/working-with-projects/working-with-images/pulling-pushing-images/) - -### Using an External Registry (Production) - -For production environments, we strongly recommend using an external, managed container registry (like ECR, GCR, ACR, or Harbor) instead of the built-in registry. - -The default `ClusterWorkflowTemplates` are designed for the internal insecure registry and cannot be used with external registries (they use `--tls-verify=false` and don't mount authentication credentials). - -To use an external registry: - -1. **Disable the built-in registry** in your Helm values: - ```yaml - registry: - enabled: false - ``` - -2. **Disable default workflow templates**: - ```yaml - global: - defaultResources: - enabled: false - ``` - -3. **Create custom `ClusterWorkflowTemplates`** that: - - Use TLS verification (remove `--tls-verify=false`) - - Mount the `registry-credentials` secret for authentication - - Configure `REGISTRY_AUTH_FILE` environment variable for podman - -4. **Create registry credentials** in the build plane namespace: - ```bash - kubectl create secret docker-registry registry-credentials \ - --docker-server= \ - --docker-username= \ - --docker-password= \ - -n openchoreo-build-plane - ``` - -See the default workflow templates in the [openchoreo repository](https://github.com/openchoreo/openchoreo/tree/main/install/helm/openchoreo-build-plane/templates/workflow-templates) as a starting point for your custom templates. - -## ClusterWorkflowTemplate - -A `ClusterWorkflowTemplate` defines a reusable build pipeline. OpenChoreo uses these templates to build different types of applications (Dockerfile, Buildpacks, etc.). When a build is triggered, OpenChoreo creates a Workflow that references the appropriate template. - -The template defines four main steps: - -### Clone - -Clones your source repository. Supports both branch and specific commit checkout: - -```yaml -- name: clone - outputs: - parameters: - - name: git-revision - valueFrom: - path: /tmp/git-revision.txt - container: - image: alpine/git - command: [sh, -c] - args: - - | - git clone --single-branch --branch "$BRANCH" --depth 1 "$REPO" /mnt/vol/source - cd /mnt/vol/source - git rev-parse HEAD | cut -c1-8 > /tmp/git-revision.txt - volumeMounts: - - name: workspace - mountPath: /mnt/vol -``` - -The short git revision is passed to subsequent steps for image tagging. - -### Build - -Builds the container image using Podman. The exact build command depends on your application type (Dockerfile, Buildpacks, etc.): - -```yaml -- name: build - inputs: - parameters: - - name: git-revision - container: - image: ghcr.io/openchoreo/podman-runner:v1.0 - command: [sh, -c] - args: - - | - IMAGE="$IMAGE_NAME:$IMAGE_TAG-{{inputs.parameters.git-revision}}" - podman build -t "$IMAGE" -f "$DOCKERFILE" "$CONTEXT" - podman save -o /mnt/vol/image.tar "$IMAGE" - securityContext: - privileged: true - volumeMounts: - - name: workspace - mountPath: /mnt/vol -``` - -The built image is saved as a tar file to the shared workspace volume. - -### Push - -Loads the image and pushes it to your registry. This step mounts the registry credentials secret: - -```yaml -- name: push - inputs: - parameters: - - name: git-revision - outputs: - parameters: - - name: image - valueFrom: - path: /tmp/image.txt - container: - image: ghcr.io/openchoreo/podman-runner:v1.0 - command: [sh, -c] - args: - - | - export REGISTRY_AUTH_FILE=/auth/.dockerconfigjson - IMAGE="$IMAGE_NAME:$IMAGE_TAG-{{inputs.parameters.git-revision}}" - podman load -i /mnt/vol/image.tar - podman tag "$IMAGE" "$REGISTRY/$IMAGE" - podman push "$REGISTRY/$IMAGE" - echo -n "$REGISTRY/$IMAGE" > /tmp/image.txt - securityContext: - privileged: true - volumeMounts: - - name: workspace - mountPath: /mnt/vol - - name: registry-auth - mountPath: /auth - readOnly: true - volumes: - - name: registry-auth - secret: - secretName: registry-credentials -``` - -The `REGISTRY_AUTH_FILE` environment variable tells Podman where to find the docker config credentials. The full image reference (registry + image + tag) is written to an output parameter for the next step. - -### Workload Create - -Generates the Workload CR using the `openchoreo-cli`. This step reads the `workload.yaml` descriptor from your source repository and combines it with the pushed image reference: - -```yaml -- name: workload-create - inputs: - parameters: - - name: image - outputs: - parameters: - - name: workload-cr - valueFrom: - path: /mnt/vol/workload-cr.yaml - container: - image: ghcr.io/openchoreo/podman-runner:v1.0 - command: [sh, -c] - args: - - | - DESCRIPTOR_PATH="/mnt/vol/source/$APP_PATH" - podman run --rm --network=none \ - -v "$DESCRIPTOR_PATH:/app:rw" -w /app \ - ghcr.io/openchoreo/openchoreo-cli:latest \ - create workload \ - --project "$PROJECT" \ - --component "$COMPONENT" \ - --image "{{inputs.parameters.image}}" \ - --descriptor "workload.yaml" \ - -o "workload-cr.yaml" - cp "$DESCRIPTOR_PATH/workload-cr.yaml" /mnt/vol/workload-cr.yaml - securityContext: - privileged: true - volumeMounts: - - name: workspace - mountPath: /mnt/vol -``` - -The CLI reads your `workload.yaml` descriptor, injects the image reference, and outputs a complete Workload CR. This CR is then applied by the Build Plane controller to trigger deployment. - -### Shared Volume - -All steps share a workspace volume for passing artifacts: - -```yaml -volumeClaimTemplates: - - metadata: - name: workspace - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 2Gi -``` - -## Data Plane Pull Configuration - -Data Planes need credentials to pull images from your registry. Create a pull secret in the Data Plane namespace: - -```bash -kubectl create secret docker-registry registry-pull-secret \ - --docker-server= \ - --docker-username= \ - --docker-password= \ - -n openchoreo-data-plane -``` - -Reference it in your DataPlane resource: - -```yaml -spec: - imagePullSecretRefs: - - "registry-pull-secret" -``` - -OpenChoreo propagates this secret to workload namespaces so deployed applications can pull images. - -For credentials that expire (like ECR tokens), use External Secrets Operator to rotate them automatically. See [Secret Management](./secret-management.mdx). - -## Troubleshooting - -**Workflow fails at push step:** - -```bash -kubectl get workflows -n openchoreo-build-plane -kubectl logs -n openchoreo-build-plane -c main -``` - -Check that `registry-credentials` secret exists and contains valid credentials. - -**Workload creation fails:** -Ensure your repository contains a valid `workload.yaml` at the configured `app-path`. - -**Data Plane cannot pull images:** - -```bash -kubectl describe pod -n -``` - -Verify `imagePullSecretRefs` in your DataPlane resource matches the secret name. diff --git a/docs/use-cases/external-container-registry.mdx b/docs/use-cases/external-container-registry.mdx new file mode 100644 index 0000000..314c223 --- /dev/null +++ b/docs/use-cases/external-container-registry.mdx @@ -0,0 +1,325 @@ +--- +title: Configure External Container Registry +description: Learn how to configure OpenChoreo to use external container registries like Docker Hub, AWS ECR, GCR, or ACR for storing and pulling container images in both build and data planes. +sidebar_position: 3 +--- + +# Configure External Container Registry + +This guide explains how to configure OpenChoreo to use an external container registry instead of the built-in one. External registries like Docker Hub, AWS ECR, Google Container Registry (GCR), or Azure Container Registry (ACR) can be used for storing container images. + +:::info Two-Plane Architecture +OpenChoreo uses separate planes for building and running applications: +- **Build Plane**: Where images are built and pushed to the registry (requires push credentials) +- **Data Plane**: Where applications run and pull images from the registry (requires pull credentials) + +Both planes need to be configured separately. +::: + +## Part 1: Configure Build Plane (Push Credentials) + +The Build Plane needs credentials to push built container images to your external registry. + +### Step 1: Install Build Plane with Registry Configuration + +When installing the build plane, configure the external registry endpoint and TLS settings: + +```bash +helm upgrade --install openchoreo-build-plane oci://ghcr.io/openchoreo/helm-charts/openchoreo-build-plane \ + --version 0.0.0-latest-dev \ + --namespace openchoreo-build-plane \ + --create-namespace \ + --set external-secrets.enabled=false \ + --set registry.enabled=false \ + --set global.defaultResources.registry.endpoint=registry.example.com \ + --set global.defaultResources.registry.tlsVerify=true +``` + +**Key Settings:** +- `registry.enabled=false` - Disables the built-in registry +- `global.defaultResources.registry.endpoint` - Your external registry endpoint +- `global.defaultResources.registry.tlsVerify` - Enable TLS verification (set to `false` only for local development) + +### Step 2: (Optional) Disable Default Workflow Templates + +If you need to customize the workflow templates, disable the default ones: + +```yaml +global: + defaultResources: + enabled: false +``` + +Then create custom `ClusterWorkflowTemplates` based on the [default templates](https://github.com/openchoreo/openchoreo/tree/main/install/helm/openchoreo-build-plane/templates/workflow-templates) in the OpenChoreo repository. + +### Step 3: Add Registry Push Credentials + +#### For Development/Testing (Fake Provider) + +Create a Docker config JSON file with your registry credentials: + +```json +{ + "auths": { + "https://index.docker.io/v1/": { + "auth": "" + } + } +} +``` + +Add the credentials to the ClusterSecretStore in the build plane: + +```bash +kubectl patch clustersecretstore default --type='json' -p='[ + { + "op": "add", + "path": "/spec/provider/fake/data/-", + "value": { + "key": ".dockerconfigjson", + "value": "{\"auths\":{\"https://index.docker.io/v1/\":{\"auth\":\"\"}}}" + } + } +]' +``` + +#### For Production + +Use a real secret backend like AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, or Google Secret Manager. Store the Docker config JSON in your secret backend and configure the ClusterSecretStore accordingly. + +### Step 4: Update ComponentWorkflow with Registry Credentials + +Add an ExternalSecret resource to your ComponentWorkflow to provide registry push credentials: + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: ComponentWorkflow +metadata: + name: docker + namespace: default +spec: + schema: + # ... schema definition + + runTemplate: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + name: ${metadata.workflowRunName} + namespace: openchoreo-ci-${metadata.orgName} + spec: + arguments: + parameters: + # ... other parameters + - name: registry-push-secret + value: ${metadata.workflowRunName}-registry-push-secret + serviceAccountName: workflow-sa + workflowTemplateRef: + clusterScope: true + name: docker + + resources: + - id: registry-push-secret + template: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: ${metadata.workflowRunName}-registry-push-secret + namespace: openchoreo-ci-${metadata.orgName} + spec: + refreshInterval: 15s + secretStoreRef: + name: default + kind: ClusterSecretStore + target: + name: ${metadata.workflowRunName}-registry-push-secret + creationPolicy: Owner + data: + - secretKey: .dockerconfigjson + remoteRef: + key: .dockerconfigjson +``` + +### Step 5: Update ClusterWorkflowTemplate Push Step + +Modify the push step in your ClusterWorkflowTemplate to use the registry credentials: + +```yaml +- name: push-step + inputs: + parameters: + - name: git-revision + outputs: + parameters: + - name: image + valueFrom: + path: /tmp/image.txt + volumes: + - name: registry-push-secret + secret: + secretName: '{{workflow.parameters.registry-push-secret}}' + container: + image: ghcr.io/openchoreo/podman-runner:v1.0 + command: [sh, -c] + securityContext: + privileged: true + volumeMounts: + - mountPath: /mnt/vol + name: workspace + - mountPath: /etc/secrets/registry-push-secret + name: registry-push-secret + readOnly: true + args: + - |- + set -e + + ##################################################################### + # 1. Inputs + ##################################################################### + GIT_REVISION={{inputs.parameters.git-revision}} + IMAGE_NAME={{workflow.parameters.image-name}} + IMAGE_TAG={{workflow.parameters.image-tag}} + SRC_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}-${GIT_REVISION}" + + ##################################################################### + # 2. Registry Configuration + ##################################################################### + REGISTRY_ENDPOINT="{{workflow.parameters.registry-endpoint}}" + AUTH_FILE="/etc/secrets/registry-push-secret/.dockerconfigjson" + + ##################################################################### + # 3. Podman Storage Configuration + ##################################################################### + mkdir -p /etc/containers + cat < /etc/containers/storage.conf + [storage] + driver = "overlay" + runroot = "/run/containers/storage" + graphroot = "/var/lib/containers/storage" + [storage.options.overlay] + mount_program = "/usr/bin/fuse-overlayfs" + EOF + + ##################################################################### + # 4. Load Image and Push to Registry + ##################################################################### + podman load -i /mnt/vol/app-image.tar + podman tag $SRC_IMAGE $REGISTRY_ENDPOINT/$SRC_IMAGE + podman push --authfile "$AUTH_FILE" --tls-verify=true $REGISTRY_ENDPOINT/$SRC_IMAGE + + ##################################################################### + # 5. Emit Image Reference + ##################################################################### + echo -n "$REGISTRY_ENDPOINT/$SRC_IMAGE" > /tmp/image.txt +``` + +**Key Changes Compared to Default Push Step in Default Templates:** + +This modified push step differs from the default in the following ways: + +1. **Mounts the registry push secret as a volume**: The `registry-push-secret` is mounted at `/etc/secrets/registry-push-secret` to provide authentication credentials +2. **Uses the secret for authentication**: The `--authfile` flag references the `.dockerconfigjson` file from the mounted secret +3. **Enables TLS verification**: Uses `--tls-verify=true` for secure communication with production registries (set to `false` only for local development) +4. **Dynamic registry endpoint**: Uses `{{workflow.parameters.registry-endpoint}}` instead of a hardcoded value, making it configurable per workflow execution + +## Part 2: Configure Data Plane (Pull Credentials) + +The Data Plane needs credentials to pull container images when deploying applications. + +### Step 1: Add Pull Credentials to Data Plane + +#### For Development/Testing (Fake Provider) + +Add the pull credentials to the ClusterSecretStore in each data plane cluster: + +```bash +kubectl patch clustersecretstore default --type='json' -p='[ + { + "op": "add", + "path": "/spec/provider/fake/data/-", + "value": { + "key": "registry-pull-credentials", + "value": "{\"auths\":{\"registry.example.com\":{\"auth\":\"\"}}}" + } + } +]' +``` + +#### For Production + +Configure the ClusterSecretStore in your data plane to use a real secret backend (same as build plane). + +### Step 2: Add imagePullSecrets to ComponentType + +Configure your ComponentType to create an ExternalSecret for pull credentials and reference it in the Deployment: + +```yaml +apiVersion: openchoreo.dev/v1alpha1 +kind: ComponentType +metadata: + name: service + namespace: default +spec: + workloadType: deployment + + schema: + # ... schema definition + + resources: + # ExternalSecret for registry pull credentials + - id: registry-pull-secret + template: + apiVersion: external-secrets.io/v1 + kind: ExternalSecret + metadata: + name: ${metadata.name}-registry-pull-secret + namespace: ${metadata.namespace} + spec: + refreshInterval: 15s + secretStoreRef: + name: default + kind: ClusterSecretStore + target: + name: ${metadata.name}-registry-pull-secret + creationPolicy: Owner + template: + type: kubernetes.io/dockerconfigjson + data: + - secretKey: .dockerconfigjson + remoteRef: + key: registry-pull-credentials + + # Deployment that uses the pull secret + - id: deployment + template: + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ${metadata.name} + namespace: ${metadata.namespace} + spec: + replicas: ${parameters.replicas} + selector: + matchLabels: + app: ${metadata.name} + template: + metadata: + labels: + app: ${metadata.name} + spec: + # Reference the pull secret + imagePullSecrets: + - name: ${metadata.name}-registry-pull-secret + containers: + - name: ${parameters.containerName} + image: ${workload.containers[parameters.containerName].image} + ports: + - containerPort: ${workload.containers[parameters.containerName].port} + # ... other container configuration +``` + +## Related Resources + +- [ComponentWorkflow API Reference](../reference/api/platform/componentworkflow.md) +- [ComponentType API Reference](../reference/api/platform/componenttype.md) +- [External Secrets Operator Documentation](https://external-secrets.io/) From 1927dd2ec5b8b85f37c07a302bb92a8d265b802e Mon Sep 17 00:00:00 2001 From: Chalindu Kodikara Date: Fri, 16 Jan 2026 18:11:25 +0530 Subject: [PATCH 6/7] Move ci docs to user guides --- .../try-it-out/on-managed-kubernetes.mdx | 2 +- .../ci/additional-resources.md | 0 .../ci/component-workflow-schema.md | 0 .../ci/custom-workflows.md | 0 .../ci}/external-container-registry.mdx | 4 +-- .../{operations => user-guide}/ci/overview.md | 2 +- .../ci/overview.png | Bin .../ci}/private-repository-multiple.md | 6 ++-- .../ci}/private-repository.mdx | 8 +++--- sidebars.ts | 27 +++++++++--------- 10 files changed, 24 insertions(+), 25 deletions(-) rename docs/{operations => user-guide}/ci/additional-resources.md (100%) rename docs/{operations => user-guide}/ci/component-workflow-schema.md (100%) rename docs/{operations => user-guide}/ci/custom-workflows.md (100%) rename docs/{use-cases => user-guide/ci}/external-container-registry.mdx (99%) rename docs/{operations => user-guide}/ci/overview.md (97%) rename docs/{operations => user-guide}/ci/overview.png (100%) rename docs/{use-cases => user-guide/ci}/private-repository-multiple.md (96%) rename docs/{use-cases => user-guide/ci}/private-repository.mdx (94%) diff --git a/docs/getting-started/try-it-out/on-managed-kubernetes.mdx b/docs/getting-started/try-it-out/on-managed-kubernetes.mdx index 0b25154..8c905cb 100644 --- a/docs/getting-started/try-it-out/on-managed-kubernetes.mdx +++ b/docs/getting-started/try-it-out/on-managed-kubernetes.mdx @@ -596,7 +596,7 @@ This guide provides a quick way to explore OpenChoreo. For production deployment 3. **Infrastructure**: Scale out and isolate your planes. - [Multi-Cluster Connectivity](../../operations/multi-cluster-connectivity.mdx) (Isolate Control Plane from Data Planes) - - [Container Registry](../../operations/container-registry.mdx) (Switch to ECR/GCR/ACR) + - [External Container Registry](../../user-guide/ci/external-container-registry.mdx) (Switch to ECR/GCR/ACR) - [Observability](../../operations/observability-alerting.mdx) (Configure persistent OpenSearch and retention) ## Next Steps diff --git a/docs/operations/ci/additional-resources.md b/docs/user-guide/ci/additional-resources.md similarity index 100% rename from docs/operations/ci/additional-resources.md rename to docs/user-guide/ci/additional-resources.md diff --git a/docs/operations/ci/component-workflow-schema.md b/docs/user-guide/ci/component-workflow-schema.md similarity index 100% rename from docs/operations/ci/component-workflow-schema.md rename to docs/user-guide/ci/component-workflow-schema.md diff --git a/docs/operations/ci/custom-workflows.md b/docs/user-guide/ci/custom-workflows.md similarity index 100% rename from docs/operations/ci/custom-workflows.md rename to docs/user-guide/ci/custom-workflows.md diff --git a/docs/use-cases/external-container-registry.mdx b/docs/user-guide/ci/external-container-registry.mdx similarity index 99% rename from docs/use-cases/external-container-registry.mdx rename to docs/user-guide/ci/external-container-registry.mdx index 314c223..e96b4a1 100644 --- a/docs/use-cases/external-container-registry.mdx +++ b/docs/user-guide/ci/external-container-registry.mdx @@ -1,7 +1,7 @@ --- -title: Configure External Container Registry +title: External Container Registry description: Learn how to configure OpenChoreo to use external container registries like Docker Hub, AWS ECR, GCR, or ACR for storing and pulling container images in both build and data planes. -sidebar_position: 3 +sidebar_position: 7 --- # Configure External Container Registry diff --git a/docs/operations/ci/overview.md b/docs/user-guide/ci/overview.md similarity index 97% rename from docs/operations/ci/overview.md rename to docs/user-guide/ci/overview.md index 3563bbf..f6bf9a8 100644 --- a/docs/operations/ci/overview.md +++ b/docs/user-guide/ci/overview.md @@ -37,7 +37,7 @@ A **ComponentWorkflowRun** represents a single execution instance of a Component ComponentWorkflowRuns are created either manually or automatically (e.g., via Git webhooks). :::warning Imperative Resource -ComponentWorkflowRun is an **imperative** resource—it triggers an action (a build) rather than declaring a desired state. Each time a ComponentWorkflowRun is applied, it initiates a new build execution. For this reason, do not include ComponentWorkflowRuns in GitOps repositories. Instead, create them through Git webhooks, the UI, or direct `kubectl apply` commands. +ComponentWorkflowRun is an **imperative** resource, it triggers an action (a build) rather than declaring a desired state. Each time a ComponentWorkflowRun is applied, it initiates a new build execution. For this reason, do not include ComponentWorkflowRuns in GitOps repositories. Instead, create them through Git webhooks, the UI, or direct `kubectl apply` commands. ::: ## Architecture diff --git a/docs/operations/ci/overview.png b/docs/user-guide/ci/overview.png similarity index 100% rename from docs/operations/ci/overview.png rename to docs/user-guide/ci/overview.png diff --git a/docs/use-cases/private-repository-multiple.md b/docs/user-guide/ci/private-repository-multiple.md similarity index 96% rename from docs/use-cases/private-repository-multiple.md rename to docs/user-guide/ci/private-repository-multiple.md index bbe36c8..38ea25f 100644 --- a/docs/use-cases/private-repository-multiple.md +++ b/docs/user-guide/ci/private-repository-multiple.md @@ -1,7 +1,7 @@ --- title: Multiple Private Git Organizations description: Configure workflows to authenticate with private repositories across multiple Git organizations, groups, or workspaces -sidebar_position: 2 +sidebar_position: 6 --- # Configure Multiple Private Git Organizations @@ -211,5 +211,5 @@ spec: ## See Also - [Private Repositories](./private-repository.mdx) - Basic private repository setup -- [Additional Resources](../operations/ci/additional-resources.md) - Working with ExternalSecrets and other resources -- [Component Workflow Schema](../operations/ci/component-workflow-schema.md) - Parameter system documentation +- [Additional Resources](./additional-resources.md) - Working with ExternalSecrets and other resources +- [Component Workflow Schema](./component-workflow-schema.md) - Parameter system documentation diff --git a/docs/use-cases/private-repository.mdx b/docs/user-guide/ci/private-repository.mdx similarity index 94% rename from docs/use-cases/private-repository.mdx rename to docs/user-guide/ci/private-repository.mdx index 9c96aae..f6f1aa7 100644 --- a/docs/use-cases/private-repository.mdx +++ b/docs/user-guide/ci/private-repository.mdx @@ -1,7 +1,7 @@ --- title: Private Git Repository description: Learn how to build applications from private Git repositories using ComponentWorkflows -sidebar_position: 1 +sidebar_position: 5 --- # Configure a Private Git Repository @@ -33,7 +33,7 @@ kubectl patch clustersecretstore default --type='json' -p='[ "path": "/spec/provider/fake/data/-", "value": { "key": "git-token", - "value": "YourGitAccessToken" + "value": "GitAccessToken" } } ]' @@ -133,6 +133,6 @@ The ComponentWorkflowRun controller creates this ExternalSecret in the Build Pla ## See Also -- [CI Overview](../operations/ci/overview.md) - Understand ComponentWorkflows and build architecture -- [Custom Workflows](../operations/ci/custom-workflows.md) - Create custom ComponentWorkflows +- [CI Overview](./overview.md) - Understand ComponentWorkflows and build architecture +- [Custom Workflows](./custom-workflows.md) - Create custom ComponentWorkflows - [External Secrets Operator](https://external-secrets.io/) - Secret management documentation diff --git a/sidebars.ts b/sidebars.ts index bc43215..aff806a 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -52,23 +52,11 @@ const sidebars: SidebarsConfig = { 'operations/backstage-configuration', 'operations/api-management', 'operations/auto-build', - 'operations/container-registry', 'operations/secret-management', - 'operations/component-workflow-secrets', 'operations/cluster-agent-rbac', 'operations/observability-alerting', 'operations/rca-agent', 'operations/upgrades', - { - type: 'category', - label: 'CI', - items: [ - 'operations/ci/overview', - 'operations/ci/component-workflow-schema', - 'operations/ci/custom-workflows', - 'operations/ci/additional-resources', - ], - }, { type: 'category', label: 'GitOps', @@ -91,8 +79,6 @@ const sidebars: SidebarsConfig = { items: [ 'use-cases/deploy-prebuilt-image', 'use-cases/api-management', - 'use-cases/private-repository', - 'use-cases/private-repository-multiple', ] }, { @@ -114,6 +100,19 @@ const sidebars: SidebarsConfig = { type: 'category', label: 'User Guide', items: [ + { + type: 'category', + label: 'CI', + items: [ + 'user-guide/ci/overview', + 'user-guide/ci/component-workflow-schema', + 'user-guide/ci/custom-workflows', + 'user-guide/ci/additional-resources', + 'user-guide/ci/private-repository', + 'user-guide/ci/private-repository-multiple', + 'user-guide/ci/external-container-registry', + ], + }, { type: 'category', label: 'Component Types', From f14f41dad1351f76026312331c8b4e516c93513a Mon Sep 17 00:00:00 2001 From: Chalindu Kodikara Date: Fri, 16 Jan 2026 18:24:09 +0530 Subject: [PATCH 7/7] Fix broken links --- .../try-it-out/on-self-hosted-kubernetes.mdx | 2 +- docs/operations/deployment-topology.mdx | 2 +- docs/operations/secret-management.mdx | 2 +- docs/user-guide/ci/custom-workflows.md | 12 ++---------- docs/user-guide/ci/external-container-registry.mdx | 4 ++-- docs/user-guide/ci/overview.md | 12 ++---------- docs/user-guide/ci/private-repository.mdx | 12 +----------- 7 files changed, 10 insertions(+), 36 deletions(-) diff --git a/docs/getting-started/try-it-out/on-self-hosted-kubernetes.mdx b/docs/getting-started/try-it-out/on-self-hosted-kubernetes.mdx index bce02a1..faec15a 100644 --- a/docs/getting-started/try-it-out/on-self-hosted-kubernetes.mdx +++ b/docs/getting-started/try-it-out/on-self-hosted-kubernetes.mdx @@ -559,7 +559,7 @@ This guide provides a quick way to explore OpenChoreo. For production deployment 3. **Infrastructure**: Scale out and isolate your planes. - [Multi-Cluster Connectivity](../../operations/multi-cluster-connectivity.mdx) (Isolate Control Plane from Data Planes) - - [Container Registry](../../operations/container-registry.mdx) (Switch to ECR/GCR/ACR) + - [Container Registry](../../user-guide/ci/external-container-registry.mdx) (Switch to ECR/GCR/ACR) - [Observability](../../operations/observability-alerting.mdx) (Configure persistent OpenSearch and retention) ## Next Steps diff --git a/docs/operations/deployment-topology.mdx b/docs/operations/deployment-topology.mdx index 0908a18..3b2dd9c 100644 --- a/docs/operations/deployment-topology.mdx +++ b/docs/operations/deployment-topology.mdx @@ -400,5 +400,5 @@ kubectl get dataplane -n -o jsonpath='{.status.condit - [Environment API Reference](/docs/reference/api/platform/environment) - [Organization API Reference](/docs/reference/api/platform/organization) - [Multi-Cluster Connectivity](./multi-cluster-connectivity.mdx) - Step-by-step multi-cluster setup -- [Container Registry](./container-registry.mdx) - Registry configuration for Build Plane +- [Container Registry](../user-guide/ci/external-container-registry.mdx) - Registry configuration for Build Plane - [Secret Management](./secret-management.mdx) - External secret store integration diff --git a/docs/operations/secret-management.mdx b/docs/operations/secret-management.mdx index 4d90ee5..ea68e13 100644 --- a/docs/operations/secret-management.mdx +++ b/docs/operations/secret-management.mdx @@ -171,6 +171,6 @@ For detailed troubleshooting guidance, see the [ESO troubleshooting documentatio ## Next Steps -- [Container Registry Configuration](./container-registry.mdx): Use External Secrets for registry credentials +- [Container Registry](../user-guide/ci/external-container-registry.mdx): Use External Secrets for registry credentials - [Deployment Topology](./deployment-topology.mdx): Configure SecretStore references in planes - [External Secrets Operator Documentation](https://external-secrets.io/): Complete ESO reference diff --git a/docs/user-guide/ci/custom-workflows.md b/docs/user-guide/ci/custom-workflows.md index 90766a8..14146e1 100644 --- a/docs/user-guide/ci/custom-workflows.md +++ b/docs/user-guide/ci/custom-workflows.md @@ -160,16 +160,8 @@ spec: ##################################################################### CLONE_URL="$REPO_URL" if [ -n "$GIT_TOKEN" ]; then - case "$REPO_URL" in - https://*) - HOST=$(echo "$REPO_URL" | sed -E 's|https://([^/]+)/.*|\1|') - REPO_PATH=$(echo "$REPO_URL" | sed -E 's|https://[^/]+/(.*)|\1|') - ;; - git@*) - HOST=$(echo "$REPO_URL" | sed -E 's|git@([^:]+):.*|\1|') - REPO_PATH=$(echo "$REPO_URL" | sed -E 's|git@[^:]+:(.*)|\1|') - ;; - esac + HOST=$(echo "$REPO_URL" | sed -E 's|https://([^/]+)/.*|\1|') + REPO_PATH=$(echo "$REPO_URL" | sed -E 's|https://[^/]+/(.*)|\1|') # Map host to authentication prefix case "$HOST" in diff --git a/docs/user-guide/ci/external-container-registry.mdx b/docs/user-guide/ci/external-container-registry.mdx index e96b4a1..2472464 100644 --- a/docs/user-guide/ci/external-container-registry.mdx +++ b/docs/user-guide/ci/external-container-registry.mdx @@ -320,6 +320,6 @@ spec: ## Related Resources -- [ComponentWorkflow API Reference](../reference/api/platform/componentworkflow.md) -- [ComponentType API Reference](../reference/api/platform/componenttype.md) +- [ComponentWorkflow API Reference](../../reference/api/platform/componentworkflow.md) +- [ComponentType API Reference](../../reference/api/platform/componenttype.md) - [External Secrets Operator Documentation](https://external-secrets.io/) diff --git a/docs/user-guide/ci/overview.md b/docs/user-guide/ci/overview.md index f6bf9a8..5665fe9 100644 --- a/docs/user-guide/ci/overview.md +++ b/docs/user-guide/ci/overview.md @@ -135,16 +135,8 @@ fi ##################################################################### CLONE_URL="$REPO_URL" if [ -n "$GIT_TOKEN" ]; then - case "$REPO_URL" in - https://*) - HOST=$(echo "$REPO_URL" | sed -E 's|https://([^/]+)/.*|\1|') - REPO_PATH=$(echo "$REPO_URL" | sed -E 's|https://[^/]+/(.*)|\1|') - ;; - git@*) - HOST=$(echo "$REPO_URL" | sed -E 's|git@([^:]+):.*|\1|') - REPO_PATH=$(echo "$REPO_URL" | sed -E 's|git@[^:]+:(.*)|\1|') - ;; - esac + HOST=$(echo "$REPO_URL" | sed -E 's|https://([^/]+)/.*|\1|') + REPO_PATH=$(echo "$REPO_URL" | sed -E 's|https://[^/]+/(.*)|\1|') # Map host to authentication prefix case "$HOST" in diff --git a/docs/user-guide/ci/private-repository.mdx b/docs/user-guide/ci/private-repository.mdx index f6f1aa7..636e3f4 100644 --- a/docs/user-guide/ci/private-repository.mdx +++ b/docs/user-guide/ci/private-repository.mdx @@ -6,7 +6,7 @@ sidebar_position: 5 # Configure a Private Git Repository -OpenChoreo's ComponentWorkflows support building from private Git repositories that require authentication. Private repository support is built-in for all default workflows and works seamlessly with GitHub, GitLab, and Bitbucket. +OpenChoreo's ComponentWorkflows support building from private Git repositories that require authentication. Private repository support is built-in for all default workflows. Follow these steps to enable private repository access: @@ -92,16 +92,6 @@ When building from a private repository: This architecture separates secret management (control plane) from build execution (build plane) while maintaining security. -### Clone Step - -The clone step automatically detects the Git provider and uses the appropriate authentication method: - -| Provider | Authentication Prefix | URL Format | -|----------|----------------------|------------| -| **GitHub** | `x-access-token` | `https://x-access-token:TOKEN@github.com/org/repo` | -| **GitLab** | `oauth2` | `https://oauth2:TOKEN@gitlab.com/org/repo` | -| **Bitbucket** | `x-token-auth` | `https://x-token-auth:TOKEN@bitbucket.org/org/repo` | - ### ExternalSecret Resource ComponentWorkflows define an ExternalSecret resource in the `resources` field: