From 028ccf90b09baa995290c1641c8876dacd493248 Mon Sep 17 00:00:00 2001 From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> Date: Mon, 1 Dec 2025 04:30:29 -0500 Subject: [PATCH 1/2] wip Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> --- docs/guides/contexts.md | 43 ++--- docs/guides/local-workstation.md | 38 +++++ docs/guides/sharing.md | 260 ++++++++++++++++++++++++++++++ docs/guides/templates.md | 180 +++++++++++++++++++++ docs/nav.yaml | 5 + docs/reference/blueprint.md | 43 ++++- docs/reference/configuration.md | 11 ++ docs/reference/contexts.md | 166 +++++++++++-------- docs/reference/features.md | 263 +++++++++++++++++++++++++++++++ docs/reference/metadata.md | 70 ++++++++ docs/reference/schema.md | 88 +++++++++++ 11 files changed, 1064 insertions(+), 103 deletions(-) create mode 100644 docs/guides/sharing.md create mode 100644 docs/guides/templates.md create mode 100644 docs/reference/features.md create mode 100644 docs/reference/metadata.md create mode 100644 docs/reference/schema.md diff --git a/docs/guides/contexts.md b/docs/guides/contexts.md index 7d10ffadf..c50d47d2c 100644 --- a/docs/guides/contexts.md +++ b/docs/guides/contexts.md @@ -1,63 +1,48 @@ --- title: "Contexts" -description: "The Windsor CLI enables a contextual workflow, dynamically reconfiguring your environment and toolchain for each specific deployment context." +description: "Understanding how contexts work in Windsor" --- # Contexts In a Windsor project, you work across different deployments or environments as "contexts". You could think of a context in terms of your software development lifecycle (SDLC) environments, such as `development`, `staging`, and `production`. You could also think of a context in terms of different pieces of your organizational infrastructure -- `admin`, `web`, or `observability`. Or, some combination of schemes. You may have a `web-staging` and `web-production` as well as `observability-staging` and `observability-production`. -Context assets are stored at `contexts//` and consist of the following : +Generally speaking, it is a good idea to stick to simple patterns. A context may represent a single role with your cloud provider and a single role with your cluster. You consider all the accounts and services associated with a context to share common administrative access. That is, you may be an administrative user of your production AWS account, and likewise have administrative access to a Kubernetes cluster running in a VPC in that same account. You consider all of this to be a part of a shared administrative context. -- Authentication details for a cloud service provider account. (_e.g._, AWS credentials) -- Authentication details for a Kubernetes cluster. (_e.g._, a kube config file) -- Encrypted secrets used to configure resources. (_e.g._, KMS-encrypted SOPS file) -- A [Blueprint](../reference/blueprint.md) resource and its associated Terraform and Kustomize configurations. - -Generally speaking, it is a good idea to stick to simple patterns. A context may represent a single role with your cloud provider and a single role with your cluster. You consider all the accounts and services associated with a context to share common administrative access. That is, you may be an administrative user of your production AWS account, and likewise have adminstrative access to a Kubernetes cluster running in a VPC in that same account. You consider all of this to be a part of a shared administrative context. - -## Creating contexts +## Creating Contexts You create a Windsor project by running `windsor init`. When doing so, it automatically creates a `local` context. This step results in the following: - Creates a new folder of assets in `contexts/local` - Adds a new entry to your project's `windsor.yaml` file at `contexts.local` -**Note:** Not all context names are handled in the same manner. Contexts named `local` or that begin with `local-` assume that you will be running a local cloud virtualization, setting defaults accordingly. You can read more in the documentation on the [local workstation](../guides/local-workstation.md). +**Note:** Not all context names are handled in the same manner. Contexts named `local` or that begin with `local-` assume that you will be running a local cloud virtualization, setting defaults accordingly. You can read more in the documentation on the [local workstation](local-workstation.md). To create another context, say one representing production, you run: -``` -windsor init production +```bash +windsor init production --provider aws ``` -This will create a folder, `contexts/production` with a basic `blueprint.yaml` file. +This will create a folder, `contexts/production` with a basic `blueprint.yaml` file and default `windsor.yaml` file. -## Switching contexts +## Switching Contexts You can switch contexts by running: -``` +```bash windsor context set ``` You can see the current context by running: -``` +```bash windsor context get ``` -Additionally, the `WINDSOR_CONTEXT` enviroment variable is available to you. +Additionally, the `WINDSOR_CONTEXT` environment variable is available to you. -
- {{ footer('Quick Start', '../../quick-start/index.html', 'Environment Injection', '../environment-injection/index.html') }} -
+## Blueprint Templates - +Contexts are generated from blueprint templates stored in `contexts/_template/`. These templates define reusable blueprint components, features, and schemas that are shared across all contexts. See the [Blueprint Templates](templates.md) guide for details on how templates work. +For detailed reference information about contexts, see the [Contexts Reference](../reference/contexts.md). diff --git a/docs/guides/local-workstation.md b/docs/guides/local-workstation.md index 2478ddae6..5b4cd9c91 100644 --- a/docs/guides/local-workstation.md +++ b/docs/guides/local-workstation.md @@ -143,6 +143,44 @@ docker tag my-image:latest ${REGISTRY_URL}/my-image:latest docker push ${REGISTRY_URL}/my-image:latest ``` +## Build ID Management + +Windsor provides a `build-id` command for managing build identifiers used for artifact tagging in local development environments. Build IDs are stored persistently in the `.windsor/.build-id` file and are available as the `BUILD_ID` environment variable and postBuild variable in Kustomizations. + +### Usage + +```bash +windsor build-id # Output current build ID +windsor build-id --new # Generate and output new build ID +``` + +### Build ID Format + +Build IDs follow the format `YYMMDD.RANDOM.#` where: +- `YYMMDD` is the current date (year, month, day) +- `RANDOM` is a random three-digit number for collision prevention +- `#` is a sequential counter incremented for each build on the same day + +### Examples + +```bash +# Get the current build ID +windsor build-id + +# Generate a new build ID and use it for Docker tagging +BUILD_ID=$(windsor build-id --new) && docker build -t myapp:$BUILD_ID . + +# Use build ID with local registry +BUILD_ID=$(windsor build-id --new) +docker build -t ${REGISTRY_URL}/myapp:$BUILD_ID . +docker push ${REGISTRY_URL}/myapp:$BUILD_ID +``` + +The `BUILD_ID` is automatically included in: +- Environment variables available to all processes +- Post-build variables in Kustomizations for Flux variable substitution +- Cluster variables accessible in Kubernetes manifests + ## Local GitOps A local GitOps workflow is provided by [git-livereload](https://github.com/windsorcli/git-livereload). When you save your files locally, they are updated within this container, and reflected as a new commit via [http://git.test](http://git.test). This feature is utilized by the internal gitops tooling ([Flux](https://github.com/fluxcd/flux2)), allowing you to persistently reflect your local Kubernetes manifests on to your local cluster. diff --git a/docs/guides/sharing.md b/docs/guides/sharing.md new file mode 100644 index 000000000..6a8f39e8d --- /dev/null +++ b/docs/guides/sharing.md @@ -0,0 +1,260 @@ +--- +title: "Sharing Blueprints" +description: "How to share and distribute blueprints using archives and OCI registries" +--- +# Sharing Blueprints + +Windsor supports sharing blueprints through two methods: local archive files (`.tar.gz`) and OCI-compatible registries. This enables you to distribute blueprints across teams, environments, and organizations. + +## Overview + +Blueprints can be shared as: +- **Local archives** (`.tar.gz` files) - For local distribution or version control +- **OCI artifacts** - For registry-based distribution compatible with Docker Hub, GitHub Container Registry, AWS ECR, and other OCI-compatible registries + +Both formats contain the same blueprint template structure and are compatible with FluxCD's OCIRepository. + +## Bundling Blueprints + +The `windsor bundle` command packages your blueprint into a `.tar.gz` archive for distribution. + +### Basic Usage + +```bash +# Bundle with automatic naming +windsor bundle -t myapp:v1.0.0 + +# Bundle to specific file +windsor bundle -t myapp:v1.0.0 -o myapp-v1.0.0.tar.gz + +# Bundle to directory (filename auto-generated) +windsor bundle -t myapp:v1.0.0 -o ./dist/ + +# Bundle using metadata.yaml for name/version +windsor bundle +``` + +### Bundle Contents + +The bundle includes all files from `contexts/_template/`: +- `_template/blueprint.yaml` - Base blueprint definition +- `_template/schema.yaml` - JSON Schema for validation (if present) +- `_template/metadata.yaml` - Blueprint metadata (if present) +- `_template/features/` - All feature definitions +- Any additional files in `_template/` (e.g., Jsonnet configs, certificates) + +### Using metadata.yaml + +If `contexts/_template/metadata.yaml` exists with a `name` field, you can bundle without specifying a tag: + +```yaml +# contexts/_template/metadata.yaml +name: my-blueprint +cliVersion: ">=0.7.1" +``` + +```bash +windsor bundle # Uses name from metadata.yaml +``` + +## Pushing to OCI Registries + +The `windsor push` command packages and pushes your blueprint to an OCI-compatible registry. + +### Prerequisites + +Before pushing, authenticate with your registry: + +```bash +# Docker Hub +docker login docker.io + +# GitHub Container Registry +docker login ghcr.io + +# AWS ECR +aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin .dkr.ecr.us-east-1.amazonaws.com +``` + +### Basic Usage + +```bash +# Push to Docker Hub +windsor push docker.io/myuser/myblueprint:v1.0.0 + +# Push to GitHub Container Registry +windsor push ghcr.io/myorg/myblueprint:v1.0.0 + +# Push using metadata.yaml for name/version +windsor push registry.example.com/blueprints +``` + +### OCI URL Format + +OCI URLs follow the format: +``` +oci://registry/repository:tag +``` + +Or when used in blueprint sources: +``` +oci://ghcr.io/windsorcli/core:v0.3.0 +``` + +The `oci://` prefix is optional when pushing (the command adds it automatically), but required when referencing in blueprint sources. + +## Using Shared Blueprints + +### From Local Archives + +Load a blueprint from a local archive when initializing a context: + +```bash +windsor init production --blueprint ./my-blueprint.tar.gz +``` + +The archive path can be: +- Absolute: `/path/to/blueprint.tar.gz` +- Relative to the current directory: `./archives/blueprint.tar.gz` + +The archive should contain a `_template` directory with blueprint files including: +- `_template/blueprint.yaml` - The base blueprint definition +- `_template/schema.yaml` - JSON Schema for configuration validation (optional) +- `_template/features/` - Feature definitions (optional) +- `_template/metadata.yaml` - Blueprint metadata including CLI version compatibility (optional) + +### From OCI Registries + +Reference OCI blueprints in your blueprint sources: + +```yaml +sources: + - name: shared-blueprint + url: oci://ghcr.io/myorg/myblueprint:v1.0.0 +``` + +When a blueprint is loaded from an OCI registry: +1. The artifact is downloaded and cached +2. Template data is extracted from `_template/` directory +3. Features are processed and merged into the base blueprint +4. Schema validation is applied if `schema.yaml` is present +5. CLI version compatibility is checked if `metadata.yaml` is present + +### OCI Source Features + +When using OCI sources, Features from the OCI artifact are automatically processed: +- Features are evaluated against your context configuration +- Only matching features are applied +- Feature inputs are merged into existing components (when `applyOnly` mode is used) + +## Artifact Structure + +Both archive and OCI formats contain the same structure: + +``` +_template/ +├── blueprint.yaml # Base blueprint +├── schema.yaml # JSON Schema (optional) +├── metadata.yaml # Metadata with CLI version (optional) +└── features/ # Feature definitions (optional) + ├── aws.yaml + └── observability.yaml +``` + +## CLI Version Compatibility + +The `cliVersion` field in `metadata.yaml` specifies the minimum required CLI version for your blueprint. This prevents users from attempting to use blueprints with incompatible CLI versions, avoiding runtime errors and ensuring that all blueprint features work correctly. + +### Why CLI Version Compatibility Matters + +As Windsor evolves, new features are added to the CLI that blueprints may depend on. For example: +- New expression functions in Features +- Enhanced schema validation capabilities +- Additional blueprint fields or merge strategies +- Changes to artifact format or OCI handling + +If a user tries to load a blueprint that requires newer CLI features with an older CLI version, they may encounter: +- Unrecognized fields or syntax +- Missing functionality +- Unexpected behavior or errors + +### Setting CLI Version Requirements + +Specify the required CLI version in `contexts/_template/metadata.yaml`: + +```yaml +name: my-blueprint +version: 1.0.0 +cliVersion: ">=0.7.1" +``` + +The `cliVersion` field uses semantic versioning constraints. Common patterns: + +- `">=0.7.1"` - Requires CLI version 0.7.1 or higher (recommended for most cases) +- `"~0.7.0"` - Requires CLI version compatible with 0.7.x (allows patch updates) +- `">=0.7.0 <0.8.0"` - Requires CLI version between 0.7.0 (inclusive) and 0.8.0 (exclusive) +- `">=0.8.0"` - Requires a specific major.minor version or higher + +### When Version Checking Occurs + +CLI version validation happens **early** in the blueprint loading process: + +1. **For OCI artifacts**: When `GetTemplateData()` extracts the artifact, it immediately validates the CLI version before processing any blueprint content +2. **For local archives**: When loading from `.tar.gz` files, validation occurs during template data extraction +3. **For OCI sources**: When processing OCI sources referenced in blueprints, each source's CLI version is validated + +If validation fails, the operation stops with a clear error message: + +``` +CLI version 0.6.5 does not satisfy required constraint '>=0.7.1' +``` + +### Best Practices + +1. **Set `cliVersion` when using new features**: If your blueprint uses features introduced in a specific CLI version, set the constraint accordingly +2. **Test with minimum version**: Verify your blueprint works with the minimum specified CLI version +3. **Update constraints carefully**: When bumping `cliVersion`, ensure you're actually using features that require it +4. **Use `>=` for forward compatibility**: Using `">=X.Y.Z"` allows users with newer CLI versions to use your blueprint +5. **Document version requirements**: Mention CLI version requirements in your blueprint's documentation + +### Example Scenarios + +**Scenario 1: Using new Features syntax** +If your blueprint uses Features with expression functions introduced in v0.7.1: + +```yaml +cliVersion: ">=0.7.1" +``` + +**Scenario 2: Backward compatibility** +If your blueprint works with any 0.7.x version but requires 0.7.0 minimum: + +```yaml +cliVersion: ">=0.7.0" +``` + +**Scenario 3: Breaking changes** +If your blueprint requires a major version due to breaking changes: + +```yaml +cliVersion: ">=0.8.0" +``` + +### What Happens Without cliVersion + +If `cliVersion` is not specified in `metadata.yaml`, the CLI will: +- Skip version validation +- Attempt to load the blueprint regardless of CLI version +- May fail with cryptic errors if incompatible features are used + +**Recommendation**: Always include `cliVersion` in your `metadata.yaml` to protect users and provide clear error messages. + +## Best Practices + +1. **Version your blueprints**: Use semantic versioning in tags (e.g., `v1.0.0`, `v1.1.0`) +2. **Include metadata.yaml**: Always include `metadata.yaml` with `name` and `cliVersion` for better compatibility checking +3. **Test before sharing**: Verify your blueprint works locally before pushing +4. **Document dependencies**: Ensure all referenced sources are accessible to users +5. **Use descriptive names**: Make blueprint names clear and descriptive +6. **Tag appropriately**: Use tags that indicate stability (e.g., `latest`, `v1.0.0`, `dev`) + diff --git a/docs/guides/templates.md b/docs/guides/templates.md new file mode 100644 index 000000000..b9b6b0b8c --- /dev/null +++ b/docs/guides/templates.md @@ -0,0 +1,180 @@ +--- +title: "Blueprint Templates" +description: "Understanding how blueprint templates work in Windsor" +--- +# Blueprint Templates + +The `contexts/_template/` directory contains blueprint template files that are shared across all contexts. This directory structure allows you to define reusable blueprint components, features, and schemas that can be customized per-context. + +## Overview + +Blueprint templates provide a way to: +- Define reusable blueprint components shared across contexts +- Use Features for conditional blueprint composition +- Validate configuration with JSON Schema +- Specify CLI version compatibility requirements + +When a context is initialized, Windsor loads the base blueprint from `_template/blueprint.yaml` and processes Features from `_template/features/` to build the final blueprint for that context. + +## Directory Structure + +``` +contexts/ +└── _template/ + ├── blueprint.yaml # Base blueprint definition + ├── schema.yaml # JSON Schema for configuration validation (optional) + ├── metadata.yaml # Blueprint metadata including CLI version compatibility (optional) + └── features/ # Feature definitions (optional) + ├── aws.yaml + ├── observability.yaml + └── ... +``` + +## Template Files + +### blueprint.yaml + +The base blueprint definition that serves as the foundation for all contexts. This file defines: +- Repository configuration +- Source definitions +- Base Terraform components +- Base Kustomizations + +When a context is initialized, this base blueprint is loaded and can be extended or overridden by context-specific configurations. + +Example: + +```yaml +kind: Blueprint +apiVersion: blueprints.windsorcli.dev/v1alpha1 +metadata: + name: base + description: Base blueprint for all contexts +repository: + url: github.com/org/blueprints + ref: + branch: main +sources: + - name: core + url: github.com/windsorcli/core + ref: + tag: v0.5.0 +terraform: + - source: core + path: cluster/talos +kustomize: + - name: ingress + path: ingress/base + source: core +``` + +### schema.yaml + +JSON Schema file that defines the expected structure and default values for configuration. The schema is used to: +- Validate user configuration values from `windsor.yaml` and `values.yaml` +- Provide default values for missing configuration keys +- Ensure configuration consistency across contexts + +The schema file must be valid JSON Schema. Supported schema versions: +- `https://json-schema.org/draft/2020-12/schema` - Standard JSON Schema Draft 2020-12 + +**Note:** Windsor implements a subset of JSON Schema Draft 2020-12. See [Blueprint Reference](../reference/blueprint.md#input-schema-validation) for supported features. + +Example: + +```yaml +$schema: https://json-schema.org/draft/2020-12/schema +type: object +properties: + provider: + type: string + default: "none" + enum: ["none", "aws", "azure", "generic"] + observability: + type: object + properties: + enabled: + type: boolean + default: false + backend: + type: string + default: "quickwit" + enum: ["quickwit", "loki", "elasticsearch"] + additionalProperties: false +additionalProperties: false +``` + +### metadata.yaml + +Blueprint metadata including CLI version compatibility constraints: + +```yaml +cliVersion: ">=0.7.1" +``` + +This ensures that blueprints are only used with compatible CLI versions. Version constraints support: +- `>=0.7.1` - Requires CLI version 0.7.1 or higher +- `~0.7.0` - Requires CLI version compatible with 0.7.x +- `>=0.7.0 <0.8.0` - Requires CLI version between 0.7.0 (inclusive) and 0.8.0 (exclusive) + +If the CLI version doesn't satisfy the constraint, blueprint loading will fail with an error. + +### features/ + +Directory containing Feature definitions. Features enable conditional blueprint composition based on configuration values. + +Features are automatically loaded from: +- `_template/features/*.yaml` - Individual feature files +- `_template/features/**/*.yaml` - Nested feature directories + +Features are processed in alphabetical order by name, then merged into the base blueprint. + +Example feature: + +```yaml +kind: Feature +apiVersion: blueprints.windsorcli.dev/v1alpha1 +metadata: + name: aws-feature + description: AWS-specific infrastructure components +when: provider == 'aws' +terraform: + - path: network/vpc + source: core + inputs: + cidr: ${network.cidr_block ?? "10.0.0.0/16"} + strategy: merge +``` + +For detailed information about Features, see the [Features Reference](../reference/features.md). + +## How Templates Work + +1. **Template Loading**: When a blueprint is loaded for a context, Windsor first loads files from `contexts/_template/` +2. **Schema Validation**: The schema from `_template/schema.yaml` (if present) validates and provides defaults for configuration values from `windsor.yaml` and `values.yaml` +3. **Feature Processing**: Features from `_template/features/` are evaluated against the context's configuration and merged into the base blueprint +4. **Context Overrides**: Context-specific `blueprint.yaml` files can override or extend the base blueprint + +## File Resolution + +Files referenced in features (via `jsonnet()` or `file()` functions) are resolved relative to the feature file location within `_template/`: +- Feature at `_template/features/aws.yaml` can reference `_template/features/config.jsonnet` +- Use `../configs/config.jsonnet` for files in parent directories +- Paths work with both local filesystem and in-memory template data (from archives) + +## Template Loading from Archives + +When loading blueprints from `.tar.gz` archives, the `_template` directory structure is preserved: + +```bash +windsor init local --blueprint ./my-blueprint.tar.gz +``` + +The archive should contain: +- `_template/blueprint.yaml` - The base blueprint definition +- `_template/schema.yaml` - JSON Schema for configuration validation (optional) +- `_template/features/` - Feature definitions (optional) +- `_template/metadata.yaml` - Blueprint metadata including CLI version compatibility (optional) + +Archive paths can be absolute or relative to the blueprint.yaml file location. + diff --git a/docs/nav.yaml b/docs/nav.yaml index b01dbcaac..83f646e6c 100644 --- a/docs/nav.yaml +++ b/docs/nav.yaml @@ -4,6 +4,8 @@ nav: - 'Quick Start': quick-start.md - 'Guides': - 'Contexts': guides/contexts.md + - 'Blueprint Templates': guides/templates.md + - 'Sharing Blueprints': guides/sharing.md - 'Environment Injection': guides/environment-injection.md - 'Local Workstation': guides/local-workstation.md - 'Secrets Management': guides/secrets-management.md @@ -17,3 +19,6 @@ nav: - 'Blueprint': reference/blueprint.md - 'Configuration': reference/configuration.md - 'Contexts': reference/contexts.md + - 'Features': reference/features.md + - 'Input Schema': reference/schema.md + - 'Blueprint Metadata': reference/metadata.md diff --git a/docs/reference/blueprint.md b/docs/reference/blueprint.md index a673a2123..af22ead96 100644 --- a/docs/reference/blueprint.md +++ b/docs/reference/blueprint.md @@ -26,6 +26,9 @@ kustomize: #... | `sources` | `[]Source` | Lists external resources referenced by the blueprint. | | `terraform` | `[]TerraformComponent` | Includes Terraform modules within the blueprint. | | `kustomize` | `[]Kustomization` | Contains Kustomization configurations in the blueprint. | +| `configMaps`| `map[string]map[string]string` | Standalone ConfigMaps to be created, not tied to specific kustomizations. These ConfigMaps are referenced by all kustomizations in PostBuild substitution. | + +For information about Features, see the [Features Reference](features.md). For schema validation, see the [Schema Reference](schema.md). For blueprint metadata, see the [Metadata Reference](metadata.md). ### Metadata Core information about the blueprint, including its identity and authors. @@ -74,16 +77,24 @@ sources: - name: oci-source url: oci://ghcr.io/windsorcli/core:v0.3.0 # No ref needed for OCI - version is in the URL + - name: archive-source + url: file://./archives/modules.tar.gz + # file:// URLs point to local tar.gz archives containing terraform modules + # The path within the archive (e.g., //terraform/modules) is automatically + # constructed from the source's pathPrefix and component path during resolution ``` | Field | Type | Description | |--------------|------------|--------------------------------------------------| | `name` | `string` | Identifies the source. | -| `url` | `string` | The source location. Supports Git URLs and OCI URLs (oci://registry/repo:tag). | -| `ref` | `Reference`| Details the branch, tag, or commit to use. Not needed for OCI URLs with embedded tags. | +| `url` | `string` | The source location. Supports Git URLs, OCI URLs (oci://registry/repo:tag), and file:// URLs for local archives. | +| `pathPrefix` | `string` | Prefix to the source path. Defaults to `terraform` if not specified. | +| `ref` | `Reference`| Details the branch, tag, or commit to use. Not needed for OCI URLs with embedded tags or file:// URLs. | | `secretName` | `string` | The secret for source access. | -**Note:** For OCI sources, the URL should include the tag/version directly (e.g., `oci://registry.example.com/repo:v1.0.0`). The `ref` field is optional for OCI sources when the tag is specified in the URL. +**Note:** +- For OCI sources, the URL should include the tag/version directly (e.g., `oci://registry.example.com/repo:v1.0.0`). The `ref` field is optional for OCI sources when the tag is specified in the URL. +- For file:// sources, the URL should be the path to a local `.tar.gz` archive file (relative to the blueprint.yaml directory or absolute), e.g., `file://./archives/modules.tar.gz`. The path within the archive (e.g., `terraform/cluster/talos`) is automatically constructed from the source's `pathPrefix` (defaults to `terraform`) and the component's `path` during resolution. The archive is automatically extracted and modules are made available for use in Terraform components. ### Reference A reference to a specific git state or version @@ -92,6 +103,7 @@ A reference to a specific git state or version reference: branch: main tag: v1.0.0 + semver: ~1.0.0 name: refs/heads/main commit: 1a2b3c4d5e6f7g8h9i0j ``` @@ -100,6 +112,7 @@ reference: |---------|--------|--------------------------------------------------| | `branch`| `string` | Branch to use. | | `tag` | `string` | Tag to use. | +| `semver`| `string` | Semantic version constraint to use (e.g., `~1.0.0`, `>=1.0.0`). | | `name` | `string` | Name of the reference. | | `commit`| `string` | Commit hash to use. | @@ -115,14 +128,19 @@ terraform: # A Terraform module defined within the current blueprint source - path: apps/my-infra parallelism: 5 + + # A Terraform module from a local archive source + - source: archive-source + path: cluster/talos ``` | Field | Type | Description | |------------|----------------------------------|--------------------------------------------------| -| `source` | `string` | Source of the Terraform module. Must be included in the list of sources. | -| `path` | `string` | Path of the Terraform module relative to the `terraform/` folder. | -| `values` | `map[string]any` | Configuration values for the module. | -| `variables`| `map[string]TerraformVariable` | Input variables for the module. | +| `source` | `string` | Source of the Terraform module. Must be included in the list of sources. Supports Git repositories, OCI artifacts, and file:// archive URLs. | +| `path` | `string` | Path of the Terraform module relative to the `terraform/` folder (for Git/OCI sources) or within the archive (for file:// sources). | +| `inputs` | `map[string]any` | Configuration values for the module. These values can be expressions using `${}` syntax (e.g., `${cluster.name}`) or literals. Values with `${}` are evaluated as expressions, plain values are passed through as literals. These are used for generating tfvars files and are not written to the final context blueprint.yaml. | +| `dependsOn`| `[]string` | Dependencies of this terraform component. | +| `destroy` | `*bool` | Determines if the component should be destroyed during down operations. Defaults to true if not specified. | | `parallelism`| `int` | Limits the number of concurrent operations as Terraform walks the graph. Corresponds to the `-parallelism` flag. | ### Kustomization @@ -154,16 +172,25 @@ kustomize: | `interval` | `*metav1.Duration` | Interval for applying the kustomization. | | `retryInterval`| `*metav1.Duration` | Retry interval for a failed kustomization. | | `timeout` | `*metav1.Duration` | Timeout for the kustomization to complete. | -| `patches` | `[]kustomize.Patch` | Patches to apply to the kustomization. | +| `patches` | `[]BlueprintPatch` | Patches to apply to the kustomization. Supports both blueprint format (path) and Flux format (patch + target). | | `wait` | `*bool` | Wait for the kustomization to be fully applied. | | `force` | `*bool` | Force apply the kustomization. | +| `prune` | `*bool` | Enable garbage collection of resources that are no longer present in the source. | | `components` | `[]string` | Components to include in the kustomization. | +| `cleanup` | `[]string` | Resources to clean up after the kustomization is applied. | +| `destroy` | `*bool` | Determines if the kustomization should be destroyed during down operations. Defaults to true if not specified. | +| `substitutions` | `map[string]string` | Values for post-build variable replacement, collected and stored in ConfigMaps for use by Flux postBuild substitution. All values are converted to strings. These are used for generating ConfigMaps and are not written to the final context blueprint.yaml. | + +#### Patches + +Patches are provided via Features, not directly in blueprint definitions. See the [Features Reference](features.md#kustomization-patches) for details on how to define patches in features. ## Cluster Variables When running `windsor install`, Kubernetes resources are applied. These resources include a configmap that introduces [post-build variables](https://fluxcd.io/flux/components/kustomize/kustomizations/#post-build-variable-substitution) into the Kubernetes manifests. These variables are outlined as follows: | Key | Description | |-------------------------|--------------------------------------------------------------------| +| `BUILD_ID` | Build identifier for artifact tagging, generated by `windsor build-id`. Format: YYMMDD.RANDOM.# | | `CONTEXT` | Specifies the context name, e.g., local. | | `DOMAIN` | The domain used for subdomain registration, e.g., test. | | `LOADBALANCER_IP_END` | The final IP in the range for load balancer assignments. | diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index 5fc7791bb..ed0b1bd8c 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -22,6 +22,17 @@ contexts: #... ## Context The context sections configure details related to each context. These configurations include cloud service providers, Kubernetes cluster drivers, and a variety of configurations involving the local cloud virtualization. Further details about these sub-configurations follow. +### Default Values + +Windsor applies default values for various configuration options when they are not explicitly specified: + +- **Cluster**: `cluster.enabled` defaults to `true` for all contexts. This ensures that cluster resources are available by default. +- **Terraform**: `terraform.enabled` defaults to `true` with a `local` backend. +- **Docker**: Docker is enabled by default with common registry configurations. +- **Provider**: Defaults to `"none"` for non-dev contexts, `"generic"` for localhost contexts. + +These defaults ensure that basic functionality is available without requiring explicit configuration. You can override any default by explicitly setting the value in your `windsor.yaml` file. + ### AWS Configuration details specific to the AWS cloud provider. Additionally, configures a Localstack service to simulate AWS resources locally. diff --git a/docs/reference/contexts.md b/docs/reference/contexts.md index b718f3c83..6b9b34c68 100644 --- a/docs/reference/contexts.md +++ b/docs/reference/contexts.md @@ -1,104 +1,138 @@ --- title: "Contexts" -description: "The Windsor CLI organizes configuration details for different deployment environments into separate context folders." +description: "Reference for context configuration and structure" --- # Contexts -Contexts represent a group of configuration details specific to a deployment environment in a Windsor project. -## Working with contexts via the cli +Contexts represent a group of configuration details specific to a deployment environment in a Windsor project. Each context has its own directory at `contexts//` containing configuration files, credentials, and generated artifacts. -You can create new contexts by running: +## Context Structure -``` -windsor init -``` - -You may then switch contexts by running: - -``` -windsor context set -``` - -You can see your current context by running: - -``` -windsor context get -``` - -The current context is also available via the `WINDSOR_CONTEXT` environment variable. - -## Context folder contents - -The `contexts/` folder contains subfolders for each context. For example, files related to configuring the local context can be found at `contexts/local`. A typical context folder may be structured as follows: +A typical context folder is structured as follows: ``` contexts/ -├── local/ -│ ├── .aws/ -│ │ └── config -│ ├── .kube/ -│ │ └── config -│ ├── .talos/ -│ │ └── config -│ │ .terraform/ -│ │ └──... -│ │ .tf_state/ -│ │ └──... -│ ├── terraform/ -│ │ ├── cluster/ -│ │ │ └── talos.tfvars -│ │ └── gitops/ -│ │ └── flux.tfvars -│ └── blueprint.yaml -└── / +└── local/ ├── .aws/ │ └── config ├── .kube/ │ └── config ├── .talos/ │ └── config - │ .terraform/ - │ └──... - │ .tf_state/ - │ └──... + ├── .terraform/ + │ └── ... + ├── .tf_state/ + │ └── ... ├── terraform/ │ ├── cluster/ │ │ └── talos.tfvars │ └── gitops/ │ └── flux.tfvars - └── blueprint.yaml + ├── blueprint.yaml + └── values.yaml +``` + +## Configuration Files + +### windsor.yaml + +The `windsor.yaml` file (or `windsor.yml`) contains static configuration that matches the `v1alpha1.Context` schema. This includes settings like cluster configuration, Docker registries, network settings, and other structured configuration options. + +Located at: +- `contexts//windsor.yaml` (context-specific) +- `windsor.yaml` (project root, with context-specific sections) + +See [Configuration Reference](configuration.md) for details. + +### values.yaml + +The `values.yaml` file is used for dynamic configuration values that don't match the static schema. This is particularly useful for: + +- Custom configuration values used by Features in blueprints +- Values that will be evaluated by expressions in Features +- Configuration that varies significantly between contexts + +Located at: `contexts//values.yaml` + +The `values.yaml` file is: +- Automatically loaded and merged with the context configuration +- Validated against the blueprint's JSON Schema (if provided) +- Available to Features for conditional logic and input evaluation +- Merged with schema defaults to provide complete configuration values + +Example `values.yaml`: + +```yaml +observability: + enabled: true + backend: quickwit + backend_url: https://quickwit.example.com +custom_settings: + feature_flag: true + api_endpoint: https://api.example.com ``` +### blueprint.yaml + +The `blueprint.yaml` file outlines references and configuration specific to the context. See [Blueprint Reference](blueprint.md) for details. + +Located at: `contexts//blueprint.yaml` + +## Directory Structure + ### `.aws/` -Contains the aws config file for authenticating with the context's AWS API. + +Contains the AWS config file for authenticating with the context's AWS API. ### `.kube/` + Contains the kubectl config file used for authenticating with the context's Kubernetes API. ### `.talos/` + Contains the talosctl config file for authenticating with the context's Talos API endpoint. ### `.terraform/` + Contains files typically used by the Terraform CLI such as modules and providers. Additionally, the `TF_DATA_DIR` resides here, along with terraform plans and state metadata files. ### `.tf_state/` + Used as the local file Terraform backend state. This is the default state until a proper remote state has been configured, or while working in a local development environment. ### `terraform/` -Contains terraform variables as `.tfvars` files. These are automatically passed to corresponding terraform projects deployed in the current context. These are explicitly referenced in the `blueprint.yaml` file. Please refer to the [Terraform](../guides/terraform.md) reference for more details. - -### `blueprint.yaml` -The `blueprint.yaml` file outlines references and configuration specific to the context. Please refer to the [blueprint](blueprint.md) documentation for more details. - -
- {{ footer('Configuration', '../configuration/index.html', 'Secrets', '../../security/secrets/index.html') }} -
- - + +Contains terraform variables as `.tfvars` files. These are automatically passed to corresponding terraform projects deployed in the current context. These are explicitly referenced in the `blueprint.yaml` file. See the [Terraform Guide](../guides/terraform.md) for more details. + +## Context Management + +### Creating Contexts + +Create new contexts by running: + +```bash +windsor init +``` + +This creates: +- A new folder at `contexts//` +- A basic `blueprint.yaml` file +- Adds a new entry to your project's `windsor.yaml` file at `contexts.` + +**Note:** Contexts named `local` or that begin with `local-` assume that you will be running a local cloud virtualization, setting defaults accordingly. + +### Switching Contexts + +Switch contexts by running: + +```bash +windsor context set +``` + +View the current context: + +```bash +windsor context get +``` + +The current context is also available via the `WINDSOR_CONTEXT` environment variable. diff --git a/docs/reference/features.md b/docs/reference/features.md new file mode 100644 index 000000000..b7d7c71cc --- /dev/null +++ b/docs/reference/features.md @@ -0,0 +1,263 @@ +--- +title: "Features" +description: "Reference for conditional blueprint fragments" +--- +# Features + +Features are conditional blueprint fragments that enable modular composition of blueprints based on user configuration values. They allow you to conditionally include Terraform components and Kustomizations in your blueprint based on expressions evaluated against your `windsor.yaml` and `values.yaml` configurations. + +## Feature Definition + +A Feature is defined in a YAML file, typically located in `_template/features/` directory of a blueprint: + +```yaml +kind: Feature +apiVersion: blueprints.windsorcli.dev/v1alpha1 +metadata: + name: aws-feature + description: AWS-specific infrastructure components +when: provider == 'aws' +terraform: + - path: network/vpc + source: core + inputs: + cidr: ${network.cidr_block ?? "10.0.0.0/16"} + enable_nat: ${network.enable_nat ?? true} + strategy: merge +kustomize: + - name: ingress + path: ingress/base + source: core + components: + - nginx + - nginx/nodeport + substitutions: + domain: ${dns.domain} + strategy: merge +``` + +| Field | Type | Description | +|-------------|-----------------------------------|-----------------------------------------------------------------------------| +| `kind` | `string` | Must be `Feature`. | +| `apiVersion`| `string` | Must be `blueprints.windsorcli.dev/v1alpha1`. | +| `metadata` | `Metadata` | Feature metadata including name and description. | +| `when` | `string` | Expression that determines if the feature should be applied. Evaluated against configuration values. If empty or evaluates to `true`, the feature is applied. | +| `terraform` | `[]ConditionalTerraformComponent` | Terraform components to include when the feature matches. | +| `kustomize` | `[]ConditionalKustomization` | Kustomizations to include when the feature matches. | + +Features inherit Repository and Sources from the base blueprint they are merged into. + +## Conditional Logic + +Features use the [Go expr library](https://github.com/expr-lang/expr) for expression evaluation. Expressions are evaluated against your configuration values from `windsor.yaml` and `values.yaml`. + +### Expression Syntax + +Expressions support: + +- **Equality/inequality**: `==`, `!=` +- **Logical operators**: `&&`, `||` +- **Parentheses for grouping**: `(expression)` +- **Nested object access**: `provider`, `cluster.enabled`, `vm.driver`, `cluster.workers.count` +- **String literals**: Use single quotes: `'aws'`, `'talos'`, `'local'` +- **Boolean values**: `true`, `false` +- **Numeric values**: `1`, `2.5` +- **Null coalescing**: `value ?? default` +- **Functions**: `values()`, `jsonnet()`, `file()` + +### Expression Examples + +```yaml +# Simple equality check +when: provider == 'aws' + +# Multiple conditions +when: provider == 'aws' && cluster.enabled == true + +# Nested property access +when: cluster.enabled == true && cluster.driver == 'talos' + +# Complex logical expressions +when: (provider == 'aws' || provider == 'azure') && cluster.enabled == true + +# Null coalescing +when: observability.enabled ?? false +``` + +## ConditionalTerraformComponent + +Extends `TerraformComponent` with conditional logic and merge strategy support. + +| Field | Type | Description | +|------------|---------------------|-----------------------------------------------------------------------------| +| `path` | `string` | Path of the Terraform module. Required. | +| `source` | `string` | Source of the Terraform module. Optional. | +| `inputs` | `map[string]any` | Configuration values for the module. Supports `${}` expressions. | +| `dependsOn`| `[]string` | Dependencies of this terraform component. | +| `destroy` | `*bool` | Determines if the component should be destroyed during down operations. | +| `parallelism`| `int` | Limits the number of concurrent operations as Terraform walks the graph. | +| `when` | `string` | Expression that determines if this component should be applied. If empty, the component is always applied when the parent feature matches. | +| `strategy` | `string` | Merge strategy: `merge` (default) or `replace`. Only available in features. | + +### Merge Strategies + +**Merge (default)**: +- Matches on `Path` and `Source` +- Deep merges `inputs` maps (overlay values override base values) +- Appends unique dependencies to `dependsOn` +- Updates `destroy` and `parallelism` if provided +- If no matching component exists, the component is appended + +**Replace**: +- Matches on `Path` and `Source` +- Completely replaces the matching component with the new one +- All existing inputs, dependencies, and settings are discarded +- If no matching component exists, the component is appended + +### Input Evaluation + +Input values support: + +- **Expressions**: Use `${}` syntax (e.g., `${cluster.workers.count}`, `${cluster.workers.count + 2}`) +- **String literals**: Plain strings are treated as literals +- **Other types**: Numbers, booleans, etc. are passed through as-is + +Expressions support: +- Direct property access: `${provider}` +- Nested access: `${cluster.workers.count}` +- Arithmetic: `${cluster.workers.count * 2}` +- Null coalescing: `${cluster.endpoint ?? "https://localhost:6443"}` +- Functions: `${values(cluster.controlplanes.nodes)}` +- File loading: `${jsonnet("config.jsonnet")}`, `${file("key.pem")}` + +## ConditionalKustomization + +Extends `Kustomization` with conditional logic and merge strategy support. + +| Field | Type | Description | +|----------------|---------------------|-----------------------------------------------------------------------------| +| `name` | `string` | Name of the kustomization. Required. | +| `path` | `string` | Path of the kustomization. Required. | +| `source` | `string` | Source of the kustomization. Optional. | +| `dependsOn` | `[]string` | Dependencies of this kustomization. | +| `components` | `[]string` | Components to include in the kustomization. | +| `patches` | `[]BlueprintPatch` | Patches to apply to the kustomization. | +| `substitutions`| `map[string]string` | Values for post-build variable replacement. Supports `${}` expressions. | +| `when` | `string` | Expression that determines if this kustomization should be applied. If empty, the kustomization is always applied when the parent feature matches. | +| `strategy` | `string` | Merge strategy: `merge` (default) or `replace`. Only available in features. | + +### Merge Strategies + +**Merge (default)**: +- Matches on `Name` +- Appends unique components to the `components` array +- Appends unique dependencies to `dependsOn` +- Updates `path`, `source`, and `destroy` if provided +- Appends patches to the existing patches array +- If no matching kustomization exists, the kustomization is appended + +**Replace**: +- Matches on `Name` +- Completely replaces the matching kustomization with the new one +- All existing components, dependencies, patches, and settings are discarded +- If no matching kustomization exists, the kustomization is appended + +### Substitutions + +Substitutions are: +- Converted to strings (as required by Flux post-build substitution) +- Stored in ConfigMaps +- Available in Kubernetes manifests via Flux's postBuild substitution +- Support `${}` expression interpolation + +### Patches + +Patches support expression interpolation in the `patch` field content using `${}` syntax. + +**Blueprint Format** (path-based): +```yaml +kustomize: + - name: my-app + path: apps/my-app + patches: + - path: patches/custom-patch.yaml +``` + +**Flux Format** (inline with target selector): +```yaml +kustomize: + - name: my-app + path: apps/my-app + patches: + - patch: |- + - op: replace + path: /spec/replicas + value: ${replicas ?? 3} + target: + kind: Deployment + name: my-app + namespace: default +``` + +Patch content in the `patch` field supports expression interpolation: +- Direct property access: `${dns.domain}` +- Nested access: `${cluster.workers.count}` +- Null coalescing: `${replicas ?? 3}` +- String interpolation: `"${context}-config"` + +When using the `merge` strategy (default), patches are appended to existing patches. When using the `replace` strategy, all existing patches are discarded and replaced with the new patches. + +## File Loading Functions + +Features support two file loading functions for dynamic configuration: + +### jsonnet() + +Loads and evaluates a Jsonnet file: + +```yaml +terraform: + - path: cluster/talos + source: core + inputs: + config: ${jsonnet("talos-config.jsonnet")} + worker_patches: ${jsonnet("talos-config.jsonnet").worker_config_patches} +``` + +- Takes a relative path to a `.jsonnet` file +- Evaluates the Jsonnet file with access to configuration values +- Returns the evaluated result (can be any JSON-compatible type) +- Paths are relative to the feature file location + +### file() + +Loads a file as a string: + +```yaml +kustomize: + - name: ingress + path: ingress/base + substitutions: + tls_cert: ${file("certs/tls.crt")} + tls_key: ${file("certs/tls.key")} +``` + +- Takes a relative path to any file +- Returns the file contents as a string +- Paths are relative to the feature file location + +### Path Resolution + +Both functions resolve paths relative to the feature file location: +- Feature at `_template/features/aws.yaml` can reference `config.jsonnet` in the same directory +- Use `../configs/config.jsonnet` for files in parent directories +- Paths work with both local filesystem and in-memory template data (from archives) + +## Feature Loading + +Features are automatically loaded from: +- `_template/features/*.yaml` - Individual feature files +- `_template/features/**/*.yaml` - Nested feature directories + +Features are processed in alphabetical order by name, then merged into the base blueprint. + diff --git a/docs/reference/metadata.md b/docs/reference/metadata.md new file mode 100644 index 000000000..21cf90093 --- /dev/null +++ b/docs/reference/metadata.md @@ -0,0 +1,70 @@ +--- +title: "Blueprint Metadata" +description: "Reference for metadata.yaml file structure and fields" +--- +# Blueprint Metadata + +Blueprints can include a `_template/metadata.yaml` file that provides additional metadata about the blueprint, including CLI version compatibility requirements. + +## Metadata File Structure + +```yaml +name: my-blueprint +version: 1.0.0 +description: A sample blueprint +author: "John Doe" +tags: + - infrastructure + - kubernetes +homepage: https://example.com/my-blueprint +license: MIT +cliVersion: ">=0.7.1" +``` + +| Field | Type | Description | +|---------------|----------|--------------------------------------------------| +| `name` | `string` | The blueprint name. Required for bundling and pushing. | +| `version` | `string` | The blueprint version. Used for artifact tagging when pushing to registries. | +| `description` | `string` | A description of the blueprint. | +| `author` | `string` | The author or maintainer of the blueprint. | +| `tags` | `[]string` | Tags for categorizing or organizing the blueprint. | +| `homepage` | `string` | URL to the blueprint's homepage or documentation. | +| `license` | `string` | License identifier (e.g., "MIT", "Apache-2.0"). | +| `cliVersion` | `string` | Semver constraint for required CLI version (e.g., ">=0.7.1", "~0.7.0"). If specified, the CLI validates that the current version satisfies the constraint before loading the blueprint. | + +## CLI Version Compatibility + +The `cliVersion` field uses semantic versioning constraints. Examples: +- `">=0.7.1"` - Requires CLI version 0.7.1 or higher +- `"~0.7.0"` - Requires CLI version compatible with 0.7.x +- `">=0.7.0 <0.8.0"` - Requires CLI version between 0.7.0 (inclusive) and 0.8.0 (exclusive) + +If the CLI version doesn't satisfy the constraint, blueprint loading will fail with an error. + +## Using Metadata in Bundling + +When using `windsor bundle` or `windsor push`, the metadata file is used to: + +1. **Automatic naming**: If `name` is specified, you can bundle without providing a tag: + ```bash + windsor bundle # Uses name from metadata.yaml + ``` + +2. **Version tagging**: The `version` field is used for artifact tagging when pushing to registries. + +3. **Compatibility checking**: The `cliVersion` field ensures users have a compatible CLI version before attempting to use the blueprint. + +## Metadata Location + +The metadata file should be placed at: +- `contexts/_template/metadata.yaml` - For local template directories +- `_template/metadata.yaml` - In blueprint archives +- Included in OCI artifacts as part of the template data + +## Best Practices + +1. **Always include `name`**: Makes bundling and sharing easier +2. **Use semantic versioning**: Follow semver for the `version` field +3. **Set `cliVersion` constraints**: Protect users from incompatible CLI versions +4. **Keep descriptions clear**: Help users understand what the blueprint does + diff --git a/docs/reference/schema.md b/docs/reference/schema.md new file mode 100644 index 000000000..8e15d825b --- /dev/null +++ b/docs/reference/schema.md @@ -0,0 +1,88 @@ +--- +title: "Input Schema Validation" +description: "Reference for JSON Schema file structure and supported features" +--- +# Input Schema Validation + +Blueprints can include a JSON Schema file (`_template/schema.yaml`) that defines the expected structure and default values for configuration. The schema is used to: + +- Validate user configuration values +- Provide default values for missing configuration keys +- Ensure configuration consistency across contexts + +## Schema Format + +The schema file must be valid JSON Schema. Supported schema versions: + +- `https://json-schema.org/draft/2020-12/schema` - Standard JSON Schema Draft 2020-12 + +**Note:** Windsor implements a subset of JSON Schema Draft 2020-12. The following features are supported: + +- **Types**: `object`, `string`, `array`, `integer`, `boolean`, `null` +- **Validation keywords**: `properties`, `required`, `enum`, `pattern` (for strings), `additionalProperties` (boolean or schema object), `items` (for arrays) +- **Default values**: `default` keyword for providing default configuration values +- **Nested objects**: Recursive validation of nested object structures + +Unsupported features include: `format`, `minimum`/`maximum`/`multipleOf`, `minLength`/`maxLength`, `minItems`/`maxItems`, `uniqueItems`, `allOf`/`anyOf`/`oneOf`/`not`, `$ref`/`$defs`, `const`, and others. + +## Example Schema + +```yaml +$schema: https://json-schema.org/draft/2020-12/schema +type: object +properties: + provider: + type: string + default: "none" + enum: ["none", "aws", "azure", "generic"] + observability: + type: object + properties: + enabled: + type: boolean + default: false + backend: + type: string + default: "quickwit" + enum: ["quickwit", "loki", "elasticsearch"] + additionalProperties: false + cluster: + type: object + properties: + enabled: + type: boolean + default: true + workers: + type: object + properties: + count: + type: integer + default: 1 + additionalProperties: false + additionalProperties: false +additionalProperties: false +``` + +## Default Values + +Default values specified in the schema are automatically merged with user configuration. When a configuration key is missing, the schema default is used. This ensures that blueprint processing always has complete configuration values. + +## Schema Loading + +The schema is automatically loaded from: +- `_template/schema.yaml` in blueprint archives or local template directories +- `schema` key in template data (for OCI artifacts) + +If no schema is provided, configuration validation is skipped and defaults are not applied. + +## Usage in Features + +Schema defaults are available when evaluating feature conditions and inputs. This means you can rely on default values being present even if users don't explicitly configure them: + +```yaml +# Feature condition can rely on schema defaults +when: observability.enabled == true && observability.backend == 'quickwit' +``` + +The schema ensures that `observability.enabled` defaults to `false` and `observability.backend` defaults to `"quickwit"` if not specified in the user's configuration. + From a5ce7ef9504996595b70ff0fa093f3bb57e8d335 Mon Sep 17 00:00:00 2001 From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> Date: Mon, 1 Dec 2025 21:58:27 -0500 Subject: [PATCH 2/2] docs: Updated docs for v0.8.0 Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> --- docs/guides/kustomize.md | 88 +++++++++- docs/guides/sharing.md | 200 ++++++++++++----------- docs/guides/templates.md | 24 +-- docs/nav.yaml | 11 +- docs/reference/features.md | 2 +- docs/reference/metadata.md | 6 +- docs/reference/schema.md | 116 ++++++++----- docs/security/secrets.md | 2 +- docs/tutorial/hello-world.md | 306 ++++++++++++++++++++++++----------- 9 files changed, 504 insertions(+), 251 deletions(-) diff --git a/docs/guides/kustomize.md b/docs/guides/kustomize.md index e0c012823..88f12da11 100644 --- a/docs/guides/kustomize.md +++ b/docs/guides/kustomize.md @@ -84,14 +84,96 @@ You can import Kustomize resources from remote sources. To import a component fr ```yaml ... kustomize: - - name: system-csi - path: system-csi + - name: csi + path: csi source: core components: - longhorn ``` -This would result in importing the `system-csi` resource from `core`, and specifically using the `longhorn` driver. By including the `source` field referencing `core`, this reference will be used when the Kustomization is generated on your cluster. +This would result in importing the `csi` resource from `core`, and specifically using the `longhorn` driver. By including the `source` field referencing `core`, this reference will be used when the Kustomization is generated on your cluster. + +## Context-Specific Patches + +Windsor automatically discovers and includes patches from your context directory. Patches placed in `contexts//patches//` are automatically discovered and applied to the corresponding kustomization. + +### Directory Structure + +Place patch files in a directory named after the kustomization: + +```plaintext +contexts/ +└── local/ + ├── blueprint.yaml + └── patches/ + └── my-app/ + ├── increase-replicas.yaml + └── add-annotations.yaml +``` + +### Patch Discovery + +All `.yaml` and `.yml` files in `contexts//patches//` are automatically discovered and added to the kustomization's patches. Windsor automatically detects the patch format: + +- **Strategic merge patches**: Standard Kubernetes resource YAML that will be merged into the base resources. See the [Kubernetes documentation on strategic merge patches](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/#create-apply-and-delete) for details. +- **JSON 6902 patches**: Patches that use a Kubernetes resource structure with `apiVersion`, `kind`, and `metadata` fields, but include a `patches` field (instead of `spec`) containing an array of JSON 6902 operations. See [RFC 6902](https://www.rfc-editor.org/rfc/rfc6902) for the JSON Patch specification. + +### Examples + +For a kustomization named `my-app`, create patches in `contexts/local/patches/my-app/`: + +**Strategic Merge Patch:** + +```yaml +# contexts/local/patches/my-app/increase-replicas.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-app +spec: + replicas: 5 +``` + +**Strategic Merge Patch with Annotations:** + +```yaml +# contexts/local/patches/my-app/add-annotations.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-app + annotations: + environment: local + managed-by: windsor +``` + +**JSON 6902 Patch:** + +Windsor also supports JSON 6902 patches using a Kubernetes resource structure with a `patches` field: + +```yaml +# contexts/local/patches/my-app/json-patch.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-app + namespace: default +patches: + - op: replace + path: /spec/replicas + value: 5 + - op: add + path: /spec/template/metadata/annotations/environment + value: local +``` + +In this format, the `apiVersion`, `kind`, and `metadata` fields identify the target resource, while the `patches` field contains an array of JSON 6902 operations (`op`, `path`, `value`). Windsor automatically extracts the target selector from the resource metadata. + +These patches are automatically discovered and applied when the blueprint is processed. Patches defined in features (via the blueprint's `features/` directory) are merged with context-specific patches, with all patches being applied in order. + +For more information on patch formats, see: +- [Kubernetes Kustomize documentation](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/) +- [RFC 6902 - JSON Patch](https://www.rfc-editor.org/rfc/rfc6902)
{{ footer('Environment Injection', '../environment-injection/index.html', 'Local Workstation', '../local-workstation/index.html') }} diff --git a/docs/guides/sharing.md b/docs/guides/sharing.md index 6a8f39e8d..805c61ffe 100644 --- a/docs/guides/sharing.md +++ b/docs/guides/sharing.md @@ -4,62 +4,17 @@ description: "How to share and distribute blueprints using archives and OCI regi --- # Sharing Blueprints -Windsor supports sharing blueprints through two methods: local archive files (`.tar.gz`) and OCI-compatible registries. This enables you to distribute blueprints across teams, environments, and organizations. +Windsor supports sharing blueprints through OCI-compatible registries, enabling you to distribute blueprints across teams, environments, and organizations. Blueprints can also be packaged as local archive files (`.tar.gz`) for troubleshooting and development purposes. ## Overview -Blueprints can be shared as: -- **Local archives** (`.tar.gz` files) - For local distribution or version control -- **OCI artifacts** - For registry-based distribution compatible with Docker Hub, GitHub Container Registry, AWS ECR, and other OCI-compatible registries +The primary method for sharing blueprints is through **OCI-compatible registries** such as Docker Hub, GitHub Container Registry, AWS ECR, and other OCI-compatible registries. This provides versioned, centralized distribution of blueprints. -Both formats contain the same blueprint template structure and are compatible with FluxCD's OCIRepository. - -## Bundling Blueprints - -The `windsor bundle` command packages your blueprint into a `.tar.gz` archive for distribution. - -### Basic Usage - -```bash -# Bundle with automatic naming -windsor bundle -t myapp:v1.0.0 - -# Bundle to specific file -windsor bundle -t myapp:v1.0.0 -o myapp-v1.0.0.tar.gz - -# Bundle to directory (filename auto-generated) -windsor bundle -t myapp:v1.0.0 -o ./dist/ - -# Bundle using metadata.yaml for name/version -windsor bundle -``` - -### Bundle Contents - -The bundle includes all files from `contexts/_template/`: -- `_template/blueprint.yaml` - Base blueprint definition -- `_template/schema.yaml` - JSON Schema for validation (if present) -- `_template/metadata.yaml` - Blueprint metadata (if present) -- `_template/features/` - All feature definitions -- Any additional files in `_template/` (e.g., Jsonnet configs, certificates) - -### Using metadata.yaml - -If `contexts/_template/metadata.yaml` exists with a `name` field, you can bundle without specifying a tag: - -```yaml -# contexts/_template/metadata.yaml -name: my-blueprint -cliVersion: ">=0.7.1" -``` - -```bash -windsor bundle # Uses name from metadata.yaml -``` +**Local archives** (`.tar.gz` files) are available for troubleshooting artifacts or local development, but are not typically used for production distribution. Both formats contain the same blueprint template structure and are compatible with FluxCD's OCIRepository. ## Pushing to OCI Registries -The `windsor push` command packages and pushes your blueprint to an OCI-compatible registry. +The `windsor push` command packages and pushes your blueprint to an OCI-compatible registry. This is the recommended method for sharing blueprints. ### Prerequisites @@ -105,9 +60,23 @@ The `oci://` prefix is optional when pushing (the command adds it automatically) ## Using Shared Blueprints +### From OCI Registries + +Reference OCI blueprints in your blueprint sources: + +```yaml +sources: + - name: shared-blueprint + url: oci://ghcr.io/myorg/myblueprint:v1.0.0 +``` + +When a blueprint is loaded from an OCI registry, Windsor downloads the artifact, extracts the template data, processes features, and validates the blueprint configuration and CLI version compatibility. + ### From Local Archives -Load a blueprint from a local archive when initializing a context: +Local archive files (`.tar.gz`) are primarily useful for troubleshooting artifacts or local development. + +To load a blueprint from a local archive when initializing a context: ```bash windsor init production --blueprint ./my-blueprint.tar.gz @@ -123,57 +92,111 @@ The archive should contain a `_template` directory with blueprint files includin - `_template/features/` - Feature definitions (optional) - `_template/metadata.yaml` - Blueprint metadata including CLI version compatibility (optional) -### From OCI Registries +## Bundling Blueprints -Reference OCI blueprints in your blueprint sources: +The `windsor bundle` command packages your blueprint into a `.tar.gz` archive. This is primarily useful for troubleshooting or local development, not typical production distribution. -```yaml -sources: - - name: shared-blueprint - url: oci://ghcr.io/myorg/myblueprint:v1.0.0 +### Basic Usage + +```bash +# Bundle with automatic naming +windsor bundle -t myapp:v1.0.0 + +# Bundle to specific file +windsor bundle -t myapp:v1.0.0 -o myapp-v1.0.0.tar.gz + +# Bundle to directory (filename auto-generated) +windsor bundle -t myapp:v1.0.0 -o ./dist/ + +# Bundle using metadata.yaml for name/version +windsor bundle ``` -When a blueprint is loaded from an OCI registry: -1. The artifact is downloaded and cached -2. Template data is extracted from `_template/` directory -3. Features are processed and merged into the base blueprint -4. Schema validation is applied if `schema.yaml` is present -5. CLI version compatibility is checked if `metadata.yaml` is present +### Bundle Contents + +The bundle includes all files from `contexts/_template/`: + +- `_template/blueprint.yaml` - Base blueprint definition +- `_template/schema.yaml` - JSON Schema for validation (if present) +- `_template/metadata.yaml` - Blueprint metadata (if present) +- `_template/features/` - All feature definitions +- Any additional files in `_template/` (e.g., Jsonnet configs, certificates) -### OCI Source Features +### Using metadata.yaml -When using OCI sources, Features from the OCI artifact are automatically processed: -- Features are evaluated against your context configuration -- Only matching features are applied -- Feature inputs are merged into existing components (when `applyOnly` mode is used) +If `contexts/_template/metadata.yaml` exists with both `name` and `version` fields, you can bundle without specifying a tag. See the [Metadata Reference](../reference/metadata.md) for complete documentation of metadata fields. + +```yaml +# contexts/_template/metadata.yaml +name: my-blueprint +cliVersion: ">=0.7.1" +``` + +```bash +windsor bundle # Uses name from metadata.yaml +``` ## Artifact Structure -Both archive and OCI formats contain the same structure: +Both archive and OCI formats contain the same structure. The artifact includes local blueprint template files and any local Terraform modules or Kustomize components in your project: ``` -_template/ -├── blueprint.yaml # Base blueprint -├── schema.yaml # JSON Schema (optional) -├── metadata.yaml # Metadata with CLI version (optional) -└── features/ # Feature definitions (optional) - ├── aws.yaml - └── observability.yaml +artifact/ +├── metadata.yaml # Artifact metadata (required) +├── _template/ # Blueprint template files +│ ├── blueprint.yaml # Base blueprint (required) +│ ├── schema.yaml # JSON Schema for validation (optional) +│ ├── metadata.yaml # Blueprint metadata (optional) +│ └── features/ # Feature definitions (optional) +│ ├── aws.yaml +│ └── observability.yaml +├── terraform/ # Local Terraform modules (if present in project) +│ └── ... +└── kustomize/ # Local Kustomize components (if present in project) + └── ... ``` +The artifact includes local Terraform modules and Kustomize components from your project's `terraform/` and `kustomize/` directories. External resources referenced via the blueprint's `sources` field (such as Git repositories or OCI artifacts) are not bundled into the artifact; they are resolved at runtime when the blueprint is used. + +### Required Files + +**`blueprint.yaml`** - The base blueprint definition that serves as the foundation for all contexts. See the [Blueprint Reference](../reference/blueprint.md) for complete documentation of blueprint structure and fields. + +### Optional Files + +**`schema.yaml`** - JSON Schema file that defines the expected structure and default values for configuration. See the [Schema Reference](../reference/schema.md) for details on supported schema features and usage. + +**`metadata.yaml`** - Blueprint metadata including name, version, and CLI version constraints. See the [Metadata Reference](../reference/metadata.md) for complete metadata options. + +**`features/`** - Directory containing Feature definitions that enable conditional blueprint composition based on configuration values. See the [Features Reference](../reference/features.md) for complete feature documentation. + +### Additional Files + +You can include any additional files in `_template/` that your features reference, such as Jsonnet files, certificates, or configuration files. These are loaded via the `${jsonnet()}` and `${file()}` functions in features. See the [Features Reference](../reference/features.md#file-loading-functions) for details on file loading. + +### Terraform and Kustomize Resources + +The artifact automatically includes all local Terraform modules from the `terraform/` directory and all local Kustomize components from the `kustomize/` directory in your project. These resources are bundled into the artifact at `terraform/` and `kustomize/` paths respectively. + +Note that external resources referenced via the blueprint's `sources` field (Git repositories, OCI artifacts, etc.) are not bundled into the artifact. These external sources are resolved at runtime when the blueprint is used, allowing blueprints to reference shared modules and components from external repositories. + +All files in `_template/`, `terraform/`, and `kustomize/` are packaged into the artifact and available when the blueprint is loaded. + ## CLI Version Compatibility -The `cliVersion` field in `metadata.yaml` specifies the minimum required CLI version for your blueprint. This prevents users from attempting to use blueprints with incompatible CLI versions, avoiding runtime errors and ensuring that all blueprint features work correctly. +The `cliVersion` field in `metadata.yaml` specifies the minimum required CLI version for your blueprint. This prevents users from attempting to use blueprints with incompatible CLI versions, avoiding runtime errors and ensuring that all blueprint features work correctly. See the [Metadata Reference](../reference/metadata.md) for complete documentation of the `cliVersion` field. ### Why CLI Version Compatibility Matters As Windsor evolves, new features are added to the CLI that blueprints may depend on. For example: + - New expression functions in Features - Enhanced schema validation capabilities - Additional blueprint fields or merge strategies - Changes to artifact format or OCI handling If a user tries to load a blueprint that requires newer CLI features with an older CLI version, they may encounter: + - Unrecognized fields or syntax - Missing functionality - Unexpected behavior or errors @@ -199,7 +222,7 @@ The `cliVersion` field uses semantic versioning constraints. Common patterns: CLI version validation happens **early** in the blueprint loading process: -1. **For OCI artifacts**: When `GetTemplateData()` extracts the artifact, it immediately validates the CLI version before processing any blueprint content +1. **For OCI artifacts**: When loading blueprints from OCI registries, the CLI version is validated immediately after downloading the artifact, before processing any blueprint content 2. **For local archives**: When loading from `.tar.gz` files, validation occurs during template data extraction 3. **For OCI sources**: When processing OCI sources referenced in blueprints, each source's CLI version is validated @@ -209,21 +232,13 @@ If validation fails, the operation stops with a clear error message: CLI version 0.6.5 does not satisfy required constraint '>=0.7.1' ``` -### Best Practices - -1. **Set `cliVersion` when using new features**: If your blueprint uses features introduced in a specific CLI version, set the constraint accordingly -2. **Test with minimum version**: Verify your blueprint works with the minimum specified CLI version -3. **Update constraints carefully**: When bumping `cliVersion`, ensure you're actually using features that require it -4. **Use `>=` for forward compatibility**: Using `">=X.Y.Z"` allows users with newer CLI versions to use your blueprint -5. **Document version requirements**: Mention CLI version requirements in your blueprint's documentation - ### Example Scenarios **Scenario 1: Using new Features syntax** -If your blueprint uses Features with expression functions introduced in v0.7.1: +If your blueprint uses Features with expression functions introduced in v0.8.0: ```yaml -cliVersion: ">=0.7.1" +cliVersion: ">=0.8.0" ``` **Scenario 2: Backward compatibility** @@ -243,6 +258,7 @@ cliVersion: ">=0.8.0" ### What Happens Without cliVersion If `cliVersion` is not specified in `metadata.yaml`, the CLI will: + - Skip version validation - Attempt to load the blueprint regardless of CLI version - May fail with cryptic errors if incompatible features are used @@ -253,8 +269,10 @@ If `cliVersion` is not specified in `metadata.yaml`, the CLI will: 1. **Version your blueprints**: Use semantic versioning in tags (e.g., `v1.0.0`, `v1.1.0`) 2. **Include metadata.yaml**: Always include `metadata.yaml` with `name` and `cliVersion` for better compatibility checking -3. **Test before sharing**: Verify your blueprint works locally before pushing -4. **Document dependencies**: Ensure all referenced sources are accessible to users -5. **Use descriptive names**: Make blueprint names clear and descriptive -6. **Tag appropriately**: Use tags that indicate stability (e.g., `latest`, `v1.0.0`, `dev`) +3. **Set `cliVersion` when using new features**: If your blueprint uses features introduced in a specific CLI version, set the constraint accordingly +4. **Test before sharing**: Verify your blueprint works locally before pushing, and test with the minimum specified CLI version +5. **Use `>=` for forward compatibility**: Using `">=X.Y.Z"` allows users with newer CLI versions to use your blueprint +6. **Document dependencies**: Ensure all referenced sources are accessible to users +7. **Use descriptive names**: Make blueprint names clear and descriptive +8. **Tag appropriately**: Use tags that indicate stability (e.g., `latest`, `v1.0.0`, `dev`) diff --git a/docs/guides/templates.md b/docs/guides/templates.md index b9b6b0b8c..71d35b7ca 100644 --- a/docs/guides/templates.md +++ b/docs/guides/templates.md @@ -35,6 +35,7 @@ contexts/ ### blueprint.yaml The base blueprint definition that serves as the foundation for all contexts. This file defines: + - Repository configuration - Source definitions - Base Terraform components @@ -71,6 +72,7 @@ kustomize: ### schema.yaml JSON Schema file that defines the expected structure and default values for configuration. The schema is used to: + - Validate user configuration values from `windsor.yaml` and `values.yaml` - Provide default values for missing configuration keys - Ensure configuration consistency across contexts @@ -78,7 +80,7 @@ JSON Schema file that defines the expected structure and default values for conf The schema file must be valid JSON Schema. Supported schema versions: - `https://json-schema.org/draft/2020-12/schema` - Standard JSON Schema Draft 2020-12 -**Note:** Windsor implements a subset of JSON Schema Draft 2020-12. See [Blueprint Reference](../reference/blueprint.md#input-schema-validation) for supported features. +**Note:** Windsor implements a subset of JSON Schema Draft 2020-12. See [Schema Reference](../reference/schema.md) for supported features. Example: @@ -113,6 +115,7 @@ cliVersion: ">=0.7.1" ``` This ensures that blueprints are only used with compatible CLI versions. Version constraints support: + - `>=0.7.1` - Requires CLI version 0.7.1 or higher - `~0.7.0` - Requires CLI version compatible with 0.7.x - `>=0.7.0 <0.8.0` - Requires CLI version between 0.7.0 (inclusive) and 0.8.0 (exclusive) @@ -158,23 +161,8 @@ For detailed information about Features, see the [Features Reference](../referen ## File Resolution Files referenced in features (via `jsonnet()` or `file()` functions) are resolved relative to the feature file location within `_template/`: + - Feature at `_template/features/aws.yaml` can reference `_template/features/config.jsonnet` - Use `../configs/config.jsonnet` for files in parent directories -- Paths work with both local filesystem and in-memory template data (from archives) - -## Template Loading from Archives - -When loading blueprints from `.tar.gz` archives, the `_template` directory structure is preserved: - -```bash -windsor init local --blueprint ./my-blueprint.tar.gz -``` - -The archive should contain: -- `_template/blueprint.yaml` - The base blueprint definition -- `_template/schema.yaml` - JSON Schema for configuration validation (optional) -- `_template/features/` - Feature definitions (optional) -- `_template/metadata.yaml` - Blueprint metadata including CLI version compatibility (optional) - -Archive paths can be absolute or relative to the blueprint.yaml file location. +- Paths work with both local filesystem and in-memory template data (from OCI artifacts) diff --git a/docs/nav.yaml b/docs/nav.yaml index 83f646e6c..42d186bff 100644 --- a/docs/nav.yaml +++ b/docs/nav.yaml @@ -4,15 +4,16 @@ nav: - 'Quick Start': quick-start.md - 'Guides': - 'Contexts': guides/contexts.md - - 'Blueprint Templates': guides/templates.md - - 'Sharing Blueprints': guides/sharing.md - - 'Environment Injection': guides/environment-injection.md - 'Local Workstation': guides/local-workstation.md - - 'Secrets Management': guides/secrets-management.md - - 'Terraform': guides/terraform.md + - 'Environment Injection': guides/environment-injection.md - 'Kustomize': guides/kustomize.md + - 'Terraform': guides/terraform.md + - 'Secrets Management': guides/secrets-management.md + - 'Blueprint Templates': guides/templates.md + - 'Sharing Blueprints': guides/sharing.md - 'Tutorial': - 'Hello, World!': tutorial/hello-world.md + - 'Sharing Your Blueprint': tutorial/sharing-blueprint.md - 'Security': - 'Trusted Folders': security/trusted-folders.md - 'Reference': diff --git a/docs/reference/features.md b/docs/reference/features.md index b7d7c71cc..12e9242e2 100644 --- a/docs/reference/features.md +++ b/docs/reference/features.md @@ -170,7 +170,7 @@ Substitutions are: - Available in Kubernetes manifests via Flux's postBuild substitution - Support `${}` expression interpolation -### Patches +### Kustomization Patches Patches support expression interpolation in the `patch` field content using `${}` syntax. diff --git a/docs/reference/metadata.md b/docs/reference/metadata.md index 21cf90093..c25e15400 100644 --- a/docs/reference/metadata.md +++ b/docs/reference/metadata.md @@ -1,5 +1,5 @@ --- -title: "Blueprint Metadata" +title: "Metadata" description: "Reference for metadata.yaml file structure and fields" --- # Blueprint Metadata @@ -45,9 +45,9 @@ If the CLI version doesn't satisfy the constraint, blueprint loading will fail w When using `windsor bundle` or `windsor push`, the metadata file is used to: -1. **Automatic naming**: If `name` is specified, you can bundle without providing a tag: +1. **Automatic naming**: If both `name` and `version` are specified, you can bundle without providing a tag: ```bash - windsor bundle # Uses name from metadata.yaml + windsor bundle # Uses name and version from metadata.yaml ``` 2. **Version tagging**: The `version` field is used for artifact tagging when pushing to registries. diff --git a/docs/reference/schema.md b/docs/reference/schema.md index 8e15d825b..a1eb0c065 100644 --- a/docs/reference/schema.md +++ b/docs/reference/schema.md @@ -1,31 +1,93 @@ --- -title: "Input Schema Validation" +title: "Schema" description: "Reference for JSON Schema file structure and supported features" --- # Input Schema Validation -Blueprints can include a JSON Schema file (`_template/schema.yaml`) that defines the expected structure and default values for configuration. The schema is used to: +Blueprints can include a JSON Schema file (`_template/schema.yaml`) that defines the expected structure and default values for configuration. -- Validate user configuration values -- Provide default values for missing configuration keys -- Ensure configuration consistency across contexts +## Schema File Structure -## Schema Format +The schema file must be valid JSON Schema Draft 2020-12. The schema is located at `_template/schema.yaml` in blueprint templates. -The schema file must be valid JSON Schema. Supported schema versions: +```yaml +$schema: https://json-schema.org/draft/2020-12/schema +type: object +properties: + # ... property definitions +``` + +## Supported Types + +Windsor supports the following JSON Schema types: + +| Type | Description | +|------|-------------| +| `object` | Key-value pairs | +| `string` | Text values | +| `array` | Ordered lists | +| `integer` | Whole numbers | +| `boolean` | True/false values | +| `null` | Null values | + +## Supported Validation Keywords + +### Type Keywords + +| Keyword | Type | Description | +|---------|------|-------------| +| `type` | `string` | Data type of the value | + +### Object Keywords + +| Keyword | Type | Description | +|---------|------|-------------| +| `properties` | `object` | Object property definitions | +| `required` | `array` | Required property names | +| `additionalProperties` | `boolean` or `object` | Control additional properties. `false` disallows, object validates | + +### String Keywords + +| Keyword | Type | Description | +|---------|------|-------------| +| `enum` | `array` | Allowed values | +| `pattern` | `string` | Regex pattern for validation | + +### Array Keywords + +| Keyword | Type | Description | +|---------|------|-------------| +| `items` | `object` | Schema for array items | -- `https://json-schema.org/draft/2020-12/schema` - Standard JSON Schema Draft 2020-12 +### Default Values -**Note:** Windsor implements a subset of JSON Schema Draft 2020-12. The following features are supported: +| Keyword | Type | Description | +|---------|------|-------------| +| `default` | `any` | Default value when property is missing | -- **Types**: `object`, `string`, `array`, `integer`, `boolean`, `null` -- **Validation keywords**: `properties`, `required`, `enum`, `pattern` (for strings), `additionalProperties` (boolean or schema object), `items` (for arrays) -- **Default values**: `default` keyword for providing default configuration values -- **Nested objects**: Recursive validation of nested object structures +## Nested Objects -Unsupported features include: `format`, `minimum`/`maximum`/`multipleOf`, `minLength`/`maxLength`, `minItems`/`maxItems`, `uniqueItems`, `allOf`/`anyOf`/`oneOf`/`not`, `$ref`/`$defs`, `const`, and others. +Nested object structures are supported recursively. Each nested object can have its own `properties`, `required`, `additionalProperties`, and `default` values. -## Example Schema +## Unsupported Features + +The following JSON Schema Draft 2020-12 features are **not** supported: + +- **Numeric constraints**: `minimum`, `maximum`, `multipleOf` +- **String length constraints**: `minLength`, `maxLength` +- **Array constraints**: `minItems`, `maxItems`, `uniqueItems` +- **Composition keywords**: `allOf`, `anyOf`, `oneOf`, `not` +- **References**: `$ref`, `$defs` +- **Format validation**: `format` keyword +- **Constants**: `const` keyword +- **Conditional validation**: `if`, `then`, `else` +- **Dependent schemas**: `dependentSchemas`, `dependentRequired` + +## Schema File Location + +The schema file must be located at `contexts/_template/schema.yaml` in your blueprint template directory. + +## Example ```yaml $schema: https://json-schema.org/draft/2020-12/schema @@ -62,27 +124,3 @@ properties: additionalProperties: false additionalProperties: false ``` - -## Default Values - -Default values specified in the schema are automatically merged with user configuration. When a configuration key is missing, the schema default is used. This ensures that blueprint processing always has complete configuration values. - -## Schema Loading - -The schema is automatically loaded from: -- `_template/schema.yaml` in blueprint archives or local template directories -- `schema` key in template data (for OCI artifacts) - -If no schema is provided, configuration validation is skipped and defaults are not applied. - -## Usage in Features - -Schema defaults are available when evaluating feature conditions and inputs. This means you can rely on default values being present even if users don't explicitly configure them: - -```yaml -# Feature condition can rely on schema defaults -when: observability.enabled == true && observability.backend == 'quickwit' -``` - -The schema ensures that `observability.enabled` defaults to `false` and `observability.backend` defaults to `"quickwit"` if not specified in the user's configuration. - diff --git a/docs/security/secrets.md b/docs/security/secrets.md index 1481c25f3..f692d61bc 100644 --- a/docs/security/secrets.md +++ b/docs/security/secrets.md @@ -4,7 +4,7 @@ description: "Best practices and special features for managing secrets securely --- # Securing Secrets -The Windsor CLI offers features and best practices to ensure the secure management of secrets within your projects. This section highlights these features and provides recommendations for securely handling secrets. Read more about [secrets management](../guides/secrets.md) in the corresponding guide. +The Windsor CLI offers features and best practices to ensure the secure management of secrets within your projects. This section highlights these features and provides recommendations for securely handling secrets. Read more about [secrets management](../guides/secrets-management.md) in the corresponding guide. ## Risks and Mitigations diff --git a/docs/tutorial/hello-world.md b/docs/tutorial/hello-world.md index a9cda3585..e52b20cc7 100644 --- a/docs/tutorial/hello-world.md +++ b/docs/tutorial/hello-world.md @@ -3,7 +3,8 @@ title: "Hello, World!" description: "Building a 'Hello, World!' page on a local cloud with the Windsor CLI" --- # Hello, World! -We'll begin with a simple "Hello, World!" demonstration to get you started with Windsor. In this tutorial, you will use the core blueprint's static website demo to serve a simple static HTML page. You should have some flavor of `npm` or `yarn` installed. + +This tutorial demonstrates building and deploying a simple "Hello, World!" web application to your local Windsor cluster. You'll learn how to build a container image, tag it using Windsor's build ID feature, push it to the local registry, and deploy it using a local Kustomize component. It is assumed you have already been through the [quick start](../quick-start.md). You have created a repository, and are able to access a local cluster. To verify this, run: @@ -20,14 +21,15 @@ worker-1 Ready 1h v1.31.4 ``` ## Build a containerized web service -Let's first begin by building the application service. This service will involve an express.js web service with livereload configured. Add the following files to your project ([source](https://github.com/windsorcli/core/tree/main/kustomize/demo/static/assets)). + +Create a simple Express.js web service. Add the following files to your project: Create `Dockerfile`: ```dockerfile FROM node:22-alpine -WORKDIR /usr/src/server +WORKDIR /usr/src/app COPY package.json package-lock.json server.js ./ RUN npm install EXPOSE 8080 @@ -38,24 +40,21 @@ Create `server.js`: ```js const express = require('express'); -const livereload = require('livereload'); -const connectLivereload = require('connect-livereload'); -const path = require('path'); - -// Create a livereload server -const liveReloadServer = livereload.createServer(); -liveReloadServer.watch('/usr/src/app'); - -// Create an express app const app = express(); -// Use connect-livereload middleware -app.use(connectLivereload()); - -// Serve static files from the '/usr/src/app' directory -app.use(express.static('/usr/src/app')); +app.get('/', (req, res) => { + res.send(` + + + Hello, World! + +

Hello, World!

+

Welcome to Windsor!

+ + + `); +}); -// Start the server const PORT = 8080; app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); @@ -66,135 +65,262 @@ Create `package.json`: ```json { - "name": "demo-static", + "name": "hello-world", "version": "1.0.0", - "description": "Demo project to run server.js using express and livereload", + "description": "Hello World web service", "main": "server.js", "scripts": { "start": "node server.js" }, "dependencies": { - "connect-livereload": "^0.6.1", - "express": "^4.21.1", - "livereload": "^0.9.3" + "express": "^4.21.1" }, - "author": "", "license": "ISC" } ``` -Create `docker-compose.yaml`: +## Tag and push to local registry +Windsor provides special features for local development: the `REGISTRY_URL` environment variable points to your local registry, and `BUILD_ID` provides unique build identifiers for artifact tagging. + +### Generate a build ID + +Windsor's build ID feature generates unique identifiers in the format `YYMMDD.RANDOM.#`. The `BUILD_ID` environment variable is automatically available through Windsor's environment injection. Generate a new build ID: + +```bash +windsor build-id --new ``` -services: - demo: - build: - context: . - image: ${REGISTRY_URL}/demo:latest -``` -Build and push the image: +The build ID is now available as the `BUILD_ID` environment variable and will be used in your Docker commands. + +### Build and tag the image + +Build your Docker image and tag it using both the build ID and the local registry URL: + +```bash +# Build the image +docker build -t hello-world:$BUILD_ID . +# Tag for local registry with build ID +docker tag hello-world:$BUILD_ID ${REGISTRY_URL}/hello-world:$BUILD_ID ``` -docker-compose build demo && \ -docker-compose push demo + +### Push to local registry + +Push the image to your local registry: + +```bash +docker push ${REGISTRY_URL}/hello-world:$BUILD_ID +docker push ${REGISTRY_URL}/hello-world:latest ``` -## Configure the static website demo component -To enable the static website demo, add the following to `contexts/local/blueprint.yaml`: +The `REGISTRY_URL` environment variable is automatically set by Windsor and points to your local registry (typically `registry.test:5000`). This registry is accessible from your Kubernetes cluster, allowing you to use locally built images in your deployments. + +## Create a local Kustomize component +Create a local Kustomize component for your hello-world application. This component will be stored in your project's `kustomize/` directory. + +Create the directory structure: + +```bash +mkdir -p kustomize/hello-world ``` -kustomize: -... -- name: demo - path: demo - dependsOn: - - ingress - force: true - components: - - bookinfo - - bookinfo/ingress - - static - - static/ingress -... -``` - -You should have added the lines `static` and `static/ingress` to the `demo` block. This will enable the static demo as well as configure its ingress. To deploy these to your local cluster, run: - -```sh -windsor install + +Create `kustomize/hello-world/deployment.yaml`: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hello-world + namespace: hello-world +spec: + replicas: 1 + selector: + matchLabels: + app: hello-world + template: + metadata: + labels: + app: hello-world + spec: + containers: + - name: hello-world + image: ${REGISTRY_URL}/hello-world:${BUILD_ID} + ports: + - containerPort: 8080 + resources: + requests: + memory: "64Mi" + cpu: "100m" + limits: + memory: "128Mi" + cpu: "200m" ``` -## Validate your resources -Check the status of all resources in the `demo-static` namespace by running: +Create `kustomize/hello-world/service.yaml`: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: hello-world + namespace: hello-world +spec: + selector: + app: hello-world + ports: + - port: 80 + targetPort: 8080 + type: ClusterIP +``` + +Create `kustomize/hello-world/namespace.yaml`: +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: hello-world ``` -kubectl get all -n demo-static + +Create `kustomize/hello-world/kustomization.yaml`: + +```yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - namespace.yaml + - deployment.yaml + - service.yaml ``` -You should see something like: +Create the ingress component directory: +```bash +mkdir -p kustomize/hello-world/ingress ``` -NAME READY STATUS RESTARTS AGE -pod/website-5b8b697588-g8zrf 1/1 Running 0 10s + +Create `kustomize/hello-world/ingress/ingress.yaml`: + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: hello-world-ingress + namespace: hello-world +spec: + rules: + - host: hello-world.${DOMAIN:-test} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: hello-world + port: + number: 80 ``` -As well as a service and ingress: +Create `kustomize/hello-world/ingress/kustomization.yaml`: +```yaml +apiVersion: kustomize.config.k8s.io/v1alpha1 +kind: Component +resources: + - ingress.yaml ``` -kubectl get svc -n demo-static + +The ingress uses the `${DOMAIN:-test}` substitution, which is automatically provided by Windsor as a postBuild substitution variable. This allows the ingress to use your configured domain (or default to `test` if not set). + +## Reference the component in your blueprint + +Add the hello-world kustomization to your `contexts/local/blueprint.yaml`. Add it to the `kustomize` section, typically after the `ingress` kustomization since hello-world depends on it: + +```yaml +kustomize: + # ... existing kustomizations from core ... + - name: ingress + path: ingress + source: core + # ... ingress configuration ... + - name: hello-world + path: hello-world + dependsOn: + - ingress + components: + - ingress + # ... other kustomizations ... ``` +The `path: hello-world` references the `kustomize/hello-world/` directory in your project. Since no `source` is specified, Windsor uses the local kustomize directory. The `dependsOn: [ingress]` ensures the ingress controller is deployed before hello-world, and `components: [ingress]` includes the ingress component we created. + +## Deploy to your cluster + +Deploy the hello-world application to your local cluster: + +```bash +windsor install ``` -kubectl get ingress -n demo-static + +This will apply the Kustomization resource to your cluster. Flux will process the kustomization and deploy your application. + +## Validate your resources + +Check the status of your hello-world deployment: + +```bash +kubectl get all -n hello-world ``` -## Create static content -Check your environment variables by running `windsor env`. You should see one with the name `PV_DEMO_STATIC_CONTENT`. This path points to a folder under `.volumes`, such as `pvc-420ddd3a-e7fd-455a-a2a4-a3388638777c`. This is where you should place your static content, or direct the output of your build command. Place this file at `${PV_DEMO_STATIC_CONTENT}/index.html`: +You should see something like: ``` - - - - - - Hello World - - -

Hello, World!

-

Welcome to the static website demo.

- - +NAME READY STATUS RESTARTS AGE +pod/hello-world-7d4f8b9c6-abc123 1/1 Running 0 30s + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/hello-world ClusterIP 10.43.123.45 80/TCP 30s ``` -Assuming everything is working as expected, you should be able to visit http://static.test:8080 and see your content appear. +## Access your application -## Inspect the environment -Let's have a look at what's been created. Run the following: +The hello-world service is deployed with an Ingress resource that makes it accessible via your local domain. Once deployed, you can access it at: ``` -kubectl describe deployment website -n demo-static +http://hello-world.test:8080 ``` -Pay attention to the `image` tag. It should read `registry.test/demo:latest`. This image was pulled from the local registry that you pushed your image to in a previous step. - -Inspect the persistent volume by running: +You can also use port forwarding for local access: +```bash +kubectl port-forward -n hello-world service/hello-world 8081:80 ``` -kubectl get pvc -n demo-static + +Then visit http://localhost:8081 + +## Inspect the deployment + +Check the deployment details: + +```bash +kubectl describe deployment hello-world -n hello-world ``` -You should see a persistent volume claim with a name matching the folder under `.volumes/`. This volume is bind mounted to your host, such that it shares content with pods under development. +Pay attention to the `image` field in the pod template. It should reference `${REGISTRY_URL}/hello-world:${BUILD_ID}`, which Flux will substitute with the actual registry URL and build ID values when applying the kustomization. + +The `REGISTRY_URL` and `BUILD_ID` variables are automatically provided by Windsor as post-build substitution variables, making it easy to reference locally built images in your Kubernetes manifests.
- {{ footer('Trusted Folders', '../../security/trusted-folders/index.html', 'Home', '../../index.html') }} + {{ footer('Quick Start', '../../quick-start/index.html', 'Sharing Your Blueprint', 'sharing-blueprint.html') }}