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/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/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/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/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/user-guide/ci/additional-resources.md b/docs/user-guide/ci/additional-resources.md new file mode 100644 index 0000000..6bbe7ab --- /dev/null +++ b/docs/user-guide/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.md) - Parameter system and schema definition +- [External Secrets Operator](https://external-secrets.io/) - Secret management documentation diff --git a/docs/user-guide/ci/component-workflow-schema.md b/docs/user-guide/ci/component-workflow-schema.md new file mode 100644 index 0000000..3e18dbb --- /dev/null +++ b/docs/user-guide/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/user-guide/ci/custom-workflows.md b/docs/user-guide/ci/custom-workflows.md new file mode 100644 index 0000000..14146e1 --- /dev/null +++ b/docs/user-guide/ci/custom-workflows.md @@ -0,0 +1,555 @@ +--- +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 + 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 + 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/user-guide/ci/external-container-registry.mdx b/docs/user-guide/ci/external-container-registry.mdx new file mode 100644 index 0000000..2472464 --- /dev/null +++ b/docs/user-guide/ci/external-container-registry.mdx @@ -0,0 +1,325 @@ +--- +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: 7 +--- + +# 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/) diff --git a/docs/user-guide/ci/overview.md b/docs/user-guide/ci/overview.md new file mode 100644 index 0000000..5665fe9 --- /dev/null +++ b/docs/user-guide/ci/overview.md @@ -0,0 +1,379 @@ +--- +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. + +:::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 + +### 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). + +:::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 + + + +### 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 + 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 + 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. diff --git a/docs/user-guide/ci/overview.png b/docs/user-guide/ci/overview.png new file mode 100644 index 0000000..28c8bd5 Binary files /dev/null and b/docs/user-guide/ci/overview.png differ diff --git a/docs/user-guide/ci/private-repository-multiple.md b/docs/user-guide/ci/private-repository-multiple.md new file mode 100644 index 0000000..38ea25f --- /dev/null +++ b/docs/user-guide/ci/private-repository-multiple.md @@ -0,0 +1,215 @@ +--- +title: Multiple Private Git Organizations +description: Configure workflows to authenticate with private repositories across multiple Git organizations, groups, or workspaces +sidebar_position: 6 +--- + +# Configure Multiple Private Git Organizations + +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 + +- **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](./additional-resources.md) - Working with ExternalSecrets and other resources +- [Component Workflow Schema](./component-workflow-schema.md) - Parameter system documentation diff --git a/docs/user-guide/ci/private-repository.mdx b/docs/user-guide/ci/private-repository.mdx new file mode 100644 index 0000000..636e3f4 --- /dev/null +++ b/docs/user-guide/ci/private-repository.mdx @@ -0,0 +1,128 @@ +--- +title: Private Git Repository +description: Learn how to build applications from private Git repositories using ComponentWorkflows +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. + +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": "GitAccessToken" + } + } +]' +``` + +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. + +### 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](./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 b2f00d6..aff806a 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -52,9 +52,7 @@ 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', @@ -102,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', @@ -138,6 +149,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 +166,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'},