diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 37d38a225..0f29581c6 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -73,7 +73,7 @@ const config: Config = { editUrl: "https://github.com/kubernetes-sigs/kro/tree/main/website", disableVersioning: false, includeCurrentVersion: true, - lastVersion: "0.6.2", + lastVersion: "0.6.3", }, blog: false, theme: { diff --git a/website/versioned_docs/version-0.6.3/docs/concepts/00-resource-group-definitions.md b/website/versioned_docs/version-0.6.3/docs/concepts/00-resource-group-definitions.md new file mode 100644 index 000000000..caf0c8438 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/docs/concepts/00-resource-group-definitions.md @@ -0,0 +1,322 @@ +--- +sidebar_position: 1 +--- + +# ResourceGraphDefinitions + +ResourceGraphDefinitions are the fundamental building blocks in **kro**. They provide a +way to define, organize, and manage sets of related Kubernetes resources as a +single, reusable unit. + +## What is a ResourceGraphDefinition? + +A **ResourceGraphDefinition** is a custom resource that lets you create new Kubernetes +APIs for deploying multiple resources together. It acts as a blueprint, +defining: + +- What users can configure (schema) +- What resources to create (resources) +- How resources reference each other (dependencies) +- When resources should be included (conditions) +- What status to expose (status) + +When you create a **ResourceGraphDefinition**, kro generates a new API (a.k.a Custom +Resource Definition) in your cluster that others can use to deploy resources in a +consistent, controlled way. + +## Anatomy of a ResourceGraphDefinition + +A ResourceGraphDefinition, like any Kubernetes resource, consists of three main parts: + +1. **Metadata**: name, labels, etc. +2. **Spec**: Defines the structure and properties of the ResourceGraphDefinition +3. **Status**: Reflects the current state of the ResourceGraphDefinition + +The `spec` section of a ResourceGraphDefinition contains two main components: + +- **Schema**: Defines what an instance of your API looks like: + - What users can configure during creation and update + - What status information they can view + - Default values and validation rules +- **Resources**: Specifies the Kubernetes resources to create: + - Resource templates + - Dependencies between resources + - Conditions for inclusion + - Readiness criteria + - [External References](#using-externalref-to-reference-objects-outside-the-resourcegraphdefinition) + +This structure translates to YAML as follows: + +```yaml +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: my-resourcegraphdefinition # Metadata section +spec: + schema: # Define your API + apiVersion: v1alpha1 # API version + kind: MyAPI # API kind + spec: {} # fields users can configure + status: {} # fields kro will populate + + # Define the resources kro will manage + resources: + - id: resource1 + # declare your resources along with default values and variables + template: {} +``` + +Let's look at each component in detail... + +## Understanding the Schema + +The schema section defines your new API's structure. It determines: + +- What fields users can configure when creating instances +- What status information they can view +- Type validation and default values + +Here's an example schema: + +```yaml +schema: + apiVersion: v1alpha1 + kind: WebApplication # This becomes your new API type + spec: + # Fields users can configure using a simple, straightforward syntax + name: string + image: string | default="nginx" + replicas: integer | default=3 + ingress: + enabled: boolean | default=false + + status: + # Fields kro will populate automatically from your resources + # Types are inferred from these CEL expressions + availableReplicas: ${deployment.status.availableReplicas} + conditions: ${deployment.status.conditions} + + validation: + # Validating admission policies added to the new API type's CRD + - expression: "${ self.image == 'nginx' || !self.ingress.enabled }" + message: "Only nginx based applications can have ingress enabled" + + additionalPrinterColumns: + # Printer columns shown for the created custom resource + - jsonPath: .status.availableReplicas + name: Available replicas + type: integer + - jsonPath: .spec.image + name: Image + type: string +``` + +**kro** follows a different approach for defining your API schema and shapes. It +leverages a human-friendly and readable syntax that is OpenAPI spec compatible. +No need to write complex OpenAPI schemas - just define your fields and types in +a straightforward way. For the complete specification of this format, check out +the [Simple Schema specification](./10-simple-schema.md). Status fields use CEL +expressions to reference fields from resources defined in your ResourceGraphDefinition. +kro automatically: + +- Infers the correct types from your expressions +- Validates that referenced resources exist +- Updates these fields as your resources change + +## Processing + +When you create a **ResourceGraphDefinition**, kro processes it in several steps to ensure +correctness and set up the necessary components: + +1. **Validation**: kro validates your **ResourceGraphDefinition** to ensure it's well + formed and follows the correct syntax, maximizing the chances of successful + deployment, and catching as many errors as possible early on. It: + + - Validates your schema definition follows the simple schema format + - Ensures all resource templates are valid Kubernetes manifests + - Checks that referenced values exist and are of the correct type + - Confirms resource dependencies form a valid Directed Acyclic Graph(DAG) + without cycles + - Validates all CEL expressions in status fields and conditions using CEL's native type system + - Validates field references exist in the actual resource schemas + - Ensures expressions return types compatible with their target fields + - Validates that CEL functions called in expressions exist and are used correctly + - Checks expression correctness and type compatibility statically without executing expressions + +2. **API Generation**: kro generates and registers a new CRD in your cluster + based on your schema. For example, if your **ResourceGraphDefinition** defines a + `WebApplication` API, kro creates a CRD that: + + - Provides API validation based on your schema definition + - Automatically applies default values you've defined + - Makes status information available to users and other systems + - Integrates seamlessly with kubectl and other Kubernetes tools + +3. **Controller Configuration**: kro configures itself to watch for instances of + your new API and their managed resources: + + - Creates all required resources following the dependency order + - Manages references and value passing between resources + - Handles the complete lifecycle for create, update, and delete operations + - Keeps status information up to date based on actual resource states + - Automatically detects and reconciles drift in managed resources + - Triggers reconciliation when any managed resource changes + +For instance, when you create a `WebApplication` ResourceGraphDefinition, kro generates +the `webapplications.kro.run` CRD. When users create instances of this API, kro +manages all the underlying resources (Deployments, Services, Custom Resources, +etc.) automatically. + +kro continuously monitors your ResourceGraphDefinition for changes, updating the API and +its behavior accordingly. + +## Instance Example + +After the **ResourceGraphDefinition** is validated and registered in the cluster, users +can create instances of it. Here's an example of how an instance for the +`WebApplication` might look: + +```yaml title="my-web-app-instance.yaml" +apiVersion: kro.run/v1alpha1 +kind: WebApplication +metadata: + name: my-web-app +spec: + appName: awesome-app + image: nginx:latest + replicas: 3 +``` + +## More about Resources + +Users can specify more controls in resources in `.spec.resources[]` + +```yaml +spec: + resources: + - id: my-resource + template || externalRef: {} # users can either template resources or reference objects outside the graph + readyWhen: + # users can specify CEL expressions to determine when a resource is ready + - ${deployment.status.conditions.exists(x, x.type == 'Available' && x.status == "True")} + includeWhen: + # users can specify CEL expressions to determine when a resource should be included in the graph + - ${schema.spec.value.enabled} +``` + +### Using `externalRef` to reference Objects outside the ResourceGraphDefinition. + +Users can specify if the object is something that is created out-of-band and needs to be referenced in the RGD. +An external reference could be specified like this: +``` +resources: + id: projectConfig + externalRef: + apiVersion: corp.platform.com/v1 + kind: Project + metadata: + name: default-project + namespace: # optional, if empty uses instance namespace +``` + +As part of processing the Resource Graph, the instance reconciler waits for the `externalRef` object to be present and reads the object from the cluster as a node in the graph. Subsequent resources can use data from this node. + + +### Using Conditional CEL Expressions (`?`) + +KRO can make use of CEL Expressions (see [this proposal for details](https://github.com/google/cel-spec/wiki/proposal-246) or look at the [CEL Implementation Reference](https://pkg.go.dev/github.com/google/cel-go/cel#hdr-Syntax_Changes-OptionalTypes)) to define optional runtime conditions for resources based on the conditional operator `?`. + +This allows you to optionally define values that have no predefined schema or are not hard dependencies in the Graph. + +#### Using `?` for referencing schema-less objects like `ConfigMap` or `Secret` + +You can use the `optional` operator to reference objects that do not have a predefined schema in the ResourceGraphDefinition. This is useful for referencing objects that may or may not exist at runtime. + +> :warning: `?` removes the ability of KRO to introspect the schema of the referenced object. Thus, it cannot wait for fields after the `?` to be present. It is recommended to use conditional expressions only for objects that are not critical to the ResourceGraphDefinition's operation or when the schema cannot be known at design time. + +A config map can be referenced like this: + +```yaml title="config-map.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: demo +data: + VALUE: "foobar" +``` + +```yaml title="external reference in ResourceGraphDefinition" +- id: external + externalRef: + apiVersion: v1 + kind: ConfigMap + metadata: + name: demo + namespace: default +``` + +With this reference, you can access the data in your schema: + +```text title="CEL Expression" +${external.data.?VALUE} +``` + +> :warning: KRO will only wait for the external reference to be present in the cluster, but it will not validate the schema of the referenced config. If the config map does not have the `VALUE` field, the expression will evaluate to `null` and might result in unexpected behavior in your application if not handled properly. + + +_For a more detailed example, see the [Optional Values & External References](../../examples/basic/optionals.md) documentation._ + +## Status Reporting + +The `status` section of a `ResourceGraphDefinition` provides information about the state of the graph and it's generated `CustomResourceDefinition` and controller. + +`status` includes a stable `Ready` condition (as well as a set of technical `status.conditions` that provide more detailed information about the state of the graph and its resources). + +:::info + +When the `Ready` condition `status` is `True`, it indicates that the ResourceGraphDefinition is valid and you can use it to create [instances](./15-instances.md). + +::: + +:::warning + +Try to only rely on the `Ready` condition, as other condition types may change frequently and are more technical in nature, can change their API over time and are generally more indicative of KRO's internal state. + +::: + +Additionally, the ResourceGraphDefinition contains a `topologicalOrder` field that provides a list of resources in the order they should be processed. This is useful for understanding the dependencies between resources and their apply order. + +Generally a status in `ResourceGraphDefinition` may look like + +```yaml +status: + conditions: + - lastTransitionTime: "2025-08-06T17:26:41Z" + message: resource graph and schema are valid + observedGeneration: 1 + reason: Valid + status: "True" + type: ResourceGraphAccepted + - lastTransitionTime: "2025-08-06T17:26:41Z" + message: kind DeploymentService has been accepted and ready + observedGeneration: 1 + reason: Ready + status: "True" + type: KindReady + - lastTransitionTime: "2025-08-06T17:26:41Z" + message: controller is running + observedGeneration: 1 + reason: Running + status: "True" + type: ControllerReady + - lastTransitionTime: "2025-08-06T17:26:41Z" + message: "" + observedGeneration: 1 + reason: Ready + status: "True" + type: Ready + state: Active + topologicalOrder: + - configmap + - deployment +``` diff --git a/website/versioned_docs/version-0.6.3/docs/concepts/10-simple-schema.md b/website/versioned_docs/version-0.6.3/docs/concepts/10-simple-schema.md new file mode 100644 index 000000000..5e41a2d02 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/docs/concepts/10-simple-schema.md @@ -0,0 +1,404 @@ +--- +sidebar_position: 2 +--- + +# Simple Schema + +**kro** follows a different approach for defining your API schema and shapes. It +leverages a human-friendly and readable syntax that is OpenAPI specification +compatible. Here's a comprehensive example: + +```yaml +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: web-application +spec: + schema: + apiVersion: v1alpha1 + kind: WebApplication + spec: + # Basic types + name: string | required=true immutable=true description="My Name" + replicas: integer | default=1 minimum=1 maximum=100 + image: string | required=true + + # Unstructured objects + values: object | required=true + + # Structured type + ingress: + enabled: boolean | default=false + host: string | default="example.com" + path: string | default="/" + + # Array type + ports: "[]integer" + + # Map type + env: "map[string]myType" + + # Custom Types + types: + myType: + value1: string | required=true + value2: integer | default=42 + + status: + # Status fields with auto-inferred types + availableReplicas: ${deployment.status.availableReplicas} + serviceEndpoint: ${service.status.loadBalancer.ingress[0].hostname} +``` + +## Type Definitions + +### Basic Types + +kro supports these foundational types: + +- `string`: Text values +- `integer`: Whole numbers +- `boolean`: True/False values +- `float`: Decimal numbers + +For example: + +```yaml +name: string +age: integer +enabled: boolean +price: float +``` + +### Structure Types + +You can create complex objects by nesting fields. Each field can use any type, +including other structures: + +```yaml +# Simple structure +address: + street: string + city: string + zipcode: string + +# Nested structures +user: + name: string + address: # Nested object + street: string + city: string + contacts: "[]string" # Array of strings +``` + +### Unstructured Objects + +Unstructured objects are declared using `object` as a type. + +:::warning + +This disables the field-validation normally offered by kro, and forwards the values to your RGD as-is. This is generally discouraged and should therefore be used with caution. In most cases, using a structured object is a better approach. + +::: + +```yaml +kind: ResourceGraphDefintion +metadata: {} +spec: + schema: + spec: + additionalHelmChartValues: object +``` + +This allows you to pass data to your CRDs directly in cases where the schema is not known in advance. This type supports any valid object, and can mix and match different primitives as well as structured types. + +```yaml +apiVersion: kro.run/v1alpha1 +kind: CRDWithUnstructuredObjects +metadata: + name: test-instance +spec: + additionalHelmChartValues: + boolean-value: true + numeric-value: 42 + structural-type: + with-additional: + nested: fields + string-value: my-string + mapping-value: + - item1 + - item2 + - item3 +``` + +### Array Types + +Arrays are denoted using `[]` syntax: + +- Basic arrays: `[]string`, `[]integer`, `[]boolean` + +Examples: + +```yaml +tags: []string +ports: []integer +``` + +### Map Types + +Maps are key-value pairs denoted as `map[keyType]valueType`: + +- `map[string]string`: String to string mapping +- `map[string]integer`: String to integer mapping + +Examples: + +```yaml +labels: "map[string]string" +metrics: "map[string]float" +``` + +### Custom Types + +Custom types are specified in the separate `types` section. +They provide a map of names to type specifications that follow the simple schema. + +Example: + +```yaml +schema: + types: + Person: + name: string + age: integer + spec: + people: '[]Person | required=true` +``` + +## Validation and Documentation + +Fields can have multiple markers for validation and documentation: + +```yaml +name: string | required=true default="app" description="Application name" +replicas: integer | default=3 minimum=1 maximum=10 +mode: string | enum="debug,info,warn,error" default="info" +``` + +### Supported Markers + +- `required=true`: Field must be provided +- `default=value`: Default value if not specified +- `description="..."`: Field documentation +- `enum="value1,value2"`: Allowed values +- `minimum=value`: Minimum value for numbers +- `maximum=value`: Maximum value for numbers +- `immutable=true`: Field cannot be changed after creation +- `pattern="regex"`: Regular expression pattern for string validation +- `minLength=number`: Minimum length for strings +- `maxLength=number`: Maximum length for strings +- `uniqueItems=true`: Ensures array elements are unique +- `minItems=number`: Minimum number of items in arrays +- `maxItems=number`: Maximum number of items in arrays + +Multiple markers can be combined using the `|` separator. + +### String Validation Markers + +String fields support additional validation markers: + +- **`pattern="regex"`**: Validates the string against a regular expression pattern +- **`minLength=number`**: Sets the minimum number of characters +- **`maxLength=number`**: Sets the maximum number of characters + +Examples: + +```yaml +# Email validation +email: string | pattern="^[\\w\\.-]+@[\\w\\.-]+\\.\\w+$" required=true + +# Username with length constraints and pattern +username: string | minLength=3 maxLength=15 pattern="^[a-zA-Z0-9_]+$" + +# Country code format +countryCode: string | pattern="^[A-Z]{2}$" minLength=2 maxLength=2 + +# Password with minimum length +password: string | minLength=8 description="Password must be at least 8 characters" +``` + +### Array Validation Markers + +Array fields support validation markers to ensure data quality: + +- **`uniqueItems=true`**: Ensures all elements in the array are unique +- **`uniqueItems=false`**: Allows duplicate elements (default behavior) +- **`minItems=number`**: Sets the minimum number of elements required in the array +- **`maxItems=number`**: Sets the maximum number of elements allowed in the array + +Examples: + +```yaml +# Unique tags with size constraints +tags: "[]string" | uniqueItems=true minItems=1 maxItems=10 description="1-10 unique tags" + +# Unique port numbers with minimum requirement +ports: "[]integer" | uniqueItems=true minItems=1 description="At least one unique port" + +# Allow duplicate comments with size limits +comments: "[]string" | uniqueItems=false maxItems=50 description="Up to 50 comments" + +# Complex validation with multiple markers +roles: "[]string" | uniqueItems=true minItems=1 maxItems=5 required=true description="1-5 unique user roles" + +# Optional array with size constraints +priorities: "[]integer" | minItems=0 maxItems=3 description="Up to 3 priority levels" +``` + +For example: + +```yaml +name: string | required=true default="app" description="Application name" +id: string | required=true immutable=true description="Unique identifier" +replicas: integer | default=3 minimum=1 maximum=10 +price: float | minimum=0.01 maximum=999.99 +mode: string | enum="debug,info,warn,error" default="info" +email: string | pattern="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" description="Valid email address" +username: string | minLength=3 maxLength=20 pattern="^[a-zA-Z0-9_]+$" +tags: "[]string" | uniqueItems=true minItems=1 maxItems=10 description="1-10 unique tags" +``` + +:::warning Floating Point Precision +When using `float` or `double` types in CEL expressions (particularly in `readyWhen` or `includeWhen` conditions), be aware of floating point precision issues that could cause unexpected behavior. Avoid comparing floating point values for equality in conditional logic. Prefer using `string`, `integer`, or `boolean` types whenever possible to avoid precision-related oscillations in resource state. +::: + +## Status Fields + +Status fields use CEL expressions to reference values from resources. kro +automatically: + +- Infers the correct types from the expressions +- Validates that referenced resources exist at ResourceGraphDefinition creation time +- Updates values when the underlying resources change +- Validates type compatibility using CEL's native type system + +```yaml +status: + # Types are inferred from the referenced fields + availableReplicas: ${deployment.status.availableReplicas} # integer + endpoint: ${service.status.loadBalancer.ingress[0].hostname} # string + metadata: ${deployment.metadata} # object +``` + +### Single vs Multi-Expression Fields + +Status fields can contain either a single CEL expression or multiple expressions concatenated together: + +**Single Expression Fields** can be any type: +```yaml +status: + replicas: ${deployment.status.replicas} # integer + metadata: ${deployment.metadata} # object + name: ${deployment.metadata.name} # string + ready: ${deployment.status.conditions.exists(c, c.type == 'Available')} # boolean +``` + +**Multi-Expression Fields** (string templating) must contain only string expressions: +```yaml +status: + # ✓ Valid - all expressions return strings + endpoint: "https://${service.metadata.name}.${service.metadata.namespace}.svc.cluster.local" + + # ✓ Valid - explicit string conversion + summary: "Replicas: ${string(deployment.status.replicas)}, Ready: ${string(deployment.status.ready)}" + + # ✗ Invalid - concatenating non-string types + invalid: "${deployment.status.replicas}-${deployment.metadata}" # Will fail validation +``` + +Multi-expression fields are useful for string templating scenarios like constructing URLs, connection strings, or IAM policies: + +```yaml +status: + iamPolicy: | + { + "Effect": "Allow", + "Resource": "arn:aws:s3:::${bucket.metadata.name}/*", + "Principal": "${serviceAccount.metadata.name}" + } +``` + +:::tip +Use explicit `string()` conversions when concatenating non-string values to ensure type compatibility. + +Alternatively, you can use CEL's built-in `format()` function for string formatting: +```yaml +status: + endpoint: ${"https://%s.%s.svc.cluster.local".format([service.metadata.name, service.metadata.namespace])} +``` + +The `${...}${...}` templating syntax is a kro convenience feature that makes common string concatenation patterns more readable. +::: + +## Default Status Fields + +kro automatically injects two fields to every instance's status: + +### 1. Conditions + +An array of condition objects tracking the instance's state: + +```yaml +status: + conditions: + - type: string # e.g., "Ready", "InstanceManaged", "GraphResolved", "ResourcesReady" + status: string # "True", "False", "Unknown" + lastTransitionTime: string + observedGeneration: integer + reason: string + message: string +``` + +kro provides a hierarchical condition structure: + +- `Ready`: Top-level condition indicating the instance is fully operational + - `InstanceManaged`: Instance finalizers and labels are properly set + - `GraphResolved`: Runtime graph has been created and resources resolved + - `ResourcesReady`: All resources in the graph are created and ready + +The `Ready` condition aggregates the state of all sub-conditions and only becomes `True` when all sub-conditions are `True`. Each condition includes an `observedGeneration` field that tracks which generation of the instance the condition reflects. + +### 2. State + +A high-level summary of the instance's status: + +```yaml +status: + state: string # ACTIVE, IN_PROGRESS, FAILED, DELETING, ERROR +``` + +:::tip + +`conditions` and `state` are reserved words. If defined in your schema, kro will +override them with its own values. + +::: + +## Additional Printer Columns + +You can define `additionalPrinterColumns` for the created CRD through the ResourceGraphDefinition by setting them on `spec.schema.additionalPrinterColumns`. + +```yaml +schema: + spec: + image: string | default="nginx" + status: + availableReplicas: ${deployment.status.availableReplicas} + additionalPrinterColumns: + - jsonPath: .status.availableReplicas + name: Available replicas + type: integer + - jsonPath: .spec.image + name: Image + type: string +``` diff --git a/website/versioned_docs/version-0.6.3/docs/concepts/15-instances.md b/website/versioned_docs/version-0.6.3/docs/concepts/15-instances.md new file mode 100644 index 000000000..462f02787 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/docs/concepts/15-instances.md @@ -0,0 +1,217 @@ +--- +sidebar_position: 15 +--- + +# Instances + +Once **kro** processes your ResourceGraphDefinition, it creates a new API in your cluster. +Users can then create instances of this API to deploy resources in a consistent, +controlled way. + +## Understanding Instances + +An instance represents your deployed application. When you create an instance, +you're telling kro "I want this set of resources running in my cluster". The +instance contains your configuration values and serves as the single source of +truth for your application's desired state. Here's an example instance of our +WebApplication API: + +```yaml +apiVersion: kro.run/v1alpha1 +kind: WebApplication +metadata: + name: my-app +spec: + name: web-app + image: nginx:latest + ingress: + enabled: true +``` + +When you create this instance, kro: + +- Creates all required resources (Deployment, Service, Ingress) +- Configures them according to your specification +- Manages them as a single unit +- Keeps their status up to date + +## How kro Manages Instances + +kro uses the standard Kubernetes reconciliation pattern to manage instances: + +1. **Observe**: Watches for changes to your instance or its resources +2. **Compare**: Checks if current state matches desired state +3. **Act**: Creates, updates, or deletes resources as needed +4. **Report**: Updates status to reflect current state + +This continuous loop ensures your resources stay in sync with your desired +state, providing features like: + +- Self-healing +- Automatic updates +- Consistent state management +- Status tracking + +### Reactive Reconciliation + +kro automatically watches all resources managed by an instance and triggers +reconciliation when any of them change. This means: + +- **Child Resource Changes**: When a managed resource (like a Deployment or Service) is + modified, kro detects the change and reconciles the instance to ensure it matches + the desired state defined in your ResourceGraphDefinition. + +- **Drift Detection**: If a resource is manually modified or deleted, kro will detect + the drift and automatically restore it to the desired state. + +- **Dependency Updates**: Changes to resources propagate through the dependency graph, + ensuring all dependent resources are updated accordingly. + +This reactive behavior ensures your instances maintain consistency without requiring +manual intervention or periodic full reconciliations. + +## Monitoring Your Instances + +KRO provides rich status information for every instance: + +```bash +$ kubectl get webapplication my-app +NAME STATUS SYNCED AGE +my-app ACTIVE true 30s +``` + +For detailed status, check the instance's YAML: + +```yaml +status: + state: ACTIVE # High-level instance state + availableReplicas: 3 # Status from Deployment + conditions: # Detailed status conditions + - lastTransitionTime: "2025-08-08T00:03:46Z" + message: instance is properly managed with finalizers and labels + observedGeneration: 1 + reason: Managed + status: "True" + type: InstanceManaged + - lastTransitionTime: "2025-08-08T00:03:46Z" + message: runtime graph created and all resources resolved + observedGeneration: 1 + reason: Resolved + status: "True" + type: GraphResolved + - lastTransitionTime: "2025-08-08T00:03:46Z" + message: all resources are created and ready + observedGeneration: 1 + reason: AllResourcesReady + status: "True" + type: ResourcesReady + - lastTransitionTime: "2025-08-08T00:03:46Z" + message: "" + observedGeneration: 1 + reason: Ready + status: "True" + type: Ready +``` + +### Understanding Status + +Every instance includes: + +1. **State**: High-level status + + - `ACTIVE`: Indicates that the instance is successfully running and active. + - `IN_PROGRESS`: Indicates that the instance is currently being processed or reconciled. + - `FAILED`: Indicates that the instance has failed to be properly reconciled. + - `DELETING`: Indicates that the instance is in the process of being deleted. + - `ERROR`: Indicates that an error occurred during instance processing. + +2. **Conditions**: Detailed status information structured hierarchically + + kro provides a top-level `Ready` condition that reflects the overall instance health. This condition is supported by three sub-conditions that track different phases of the reconciliation process: + + - `InstanceManaged`: Instance finalizers and labels are properly set + - Ensures the instance is under kro's management + - Tracks whether cleanup handlers (finalizers) are configured + - Confirms instance is labeled with ownership and version information + + - `GraphResolved`: Runtime graph has been created and resources resolved + - Validates that the resource graph has been successfully parsed + - Confirms all resource templates have been resolved + - Ensures dependencies between resources are properly understood + + - `ResourcesReady`: All resources in the graph are created and ready + - Tracks the creation and readiness of all managed resources + - Monitors the health of resources in topological order + - Reports when all resources have reached their ready state + + - `Ready`: Instance is fully operational (top-level condition) + - Aggregates the state of all sub-conditions + - Only becomes True when all sub-conditions are True + - **The primary condition to monitor and wait on for instance health** + - Use this condition in automation, CI/CD, and health checks + + :::tip + Always use the `Ready` condition to determine instance health. The sub-conditions (`InstanceManaged`, `GraphResolved`, `ResourcesReady`) are provided for debugging purposes and may change in future versions. kro reserves the right to add, remove, or modify sub-conditions without breaking compatibility as long as the `Ready` condition behavior remains stable. + ::: + + Each condition includes: + - `observedGeneration`: Tracks which generation of the instance this condition reflects + - `lastTransitionTime`: When the condition last changed state + - `reason`: A programmatic identifier for the condition state + - `message`: A human-readable description of the current state + +3. **Resource Status**: Status from your resources + - Values you defined in your ResourceGraphDefinition's status section + - Automatically updated as resources change + +## Debugging Instance Issues + +When an instance is not in the expected state, the condition hierarchy helps you quickly identify where the problem occurred: + +1. **Check the Ready condition first** + ```bash + kubectl get -o jsonpath='{.status.conditions[?(@.type=="Ready")]}' + ``` + +2. **If Ready is False, check the sub-conditions** to identify which phase failed: + + - If `InstanceManaged` is False: Check if there are issues with finalizers or instance labels + - If `GraphResolved` is False: The resource graph could not be created - check the ResourceGraphDefinition for syntax errors or invalid CEL expressions + - If `ResourcesReady` is False: One or more managed resources failed to become ready - check the error message for which resource failed + +3. **Use kubectl describe** to see all conditions and recent events: + ```bash + kubectl describe + ``` + +4. **Check the observedGeneration** field in conditions: + - If `observedGeneration` is less than `metadata.generation`, the controller hasn't processed the latest changes yet + - If they match, the conditions reflect the current state of your instance + +## Best Practices + +- **Version Control**: Keep your instance definitions in version control + alongside your application code. This helps track changes, rollback when + needed, and maintain configuration history. + +- **Use Labels Effectively**: Add meaningful labels to your instances for better + organization, filtering, and integration with other tools. kro propagates + labels to the sub resources for easy identification. + +- **Active Monitoring**: Regularly check instance status beyond just "Running". + Watch conditions, resource status, and events to catch potential issues early + and understand your application's health. Focus on the `Ready` condition and + its sub-conditions to understand the reconciliation state. + +- **Monitor observedGeneration**: When making changes to an instance, verify that + `observedGeneration` in the conditions matches `metadata.generation` to ensure + kro has processed your changes. + +- **Leverage Reactive Reconciliation**: kro automatically detects and corrects drift + in managed resources. If you need to make manual changes to resources, update the + instance specification instead to ensure changes persist and align with your + desired state. + +- **Regular Reviews**: Periodically review your instance configurations to + ensure they reflect current requirements and best practices. Update resource + requests, limits, and other configurations as your application needs evolve. diff --git a/website/versioned_docs/version-0.6.3/docs/concepts/20-access-control.md b/website/versioned_docs/version-0.6.3/docs/concepts/20-access-control.md new file mode 100644 index 000000000..eb5878c47 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/docs/concepts/20-access-control.md @@ -0,0 +1,75 @@ +--- +sidebar_position: 20 +--- + +# Access Control + +There are currently two modes of access control supported by **kro**, if you +[install through the Helm chart](../getting-started/01-Installation.md#install-kro-using-helm): + +- `unrestricted` + +- `aggregation` + +The mode is selected with a `values` property `rbac.mode`, and defaults to `unrestricted`. + +## `unrestricted` Access + +In the `unrestricted` access mode, the chart includes a `ClusterRole` granting +**kro** _full control to every resource type in your cluster_. This can be +useful for experimenting in a test environment, where access control is not +necessary, but is not recommended in a production environment. + +In this mode, anyone with access to create `ResourceGraphDefinition` resources, +effectively also has admin access to the cluster. + +## `aggregation` Access + +In the `aggregation` access mode, the chart includes an [_aggregated_ `ClusterRole`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles) +which dynamically includes all rules from all `ClusterRoles` that have the label +`rbac.kro.run/aggregate-to-controller: "true"`. + +There is a very minimal set of permissions provisioned by the chart itself, just +enough to let **kro** run at all: full permissions for `ResourceGraphDefinition`s +and its subresources, and full permissions for `CustomResourceDefinitions` as +**kro** will create them in response to the existence of an RGD. + +However, this does _not_ automatically set up permissions for **kro** to actually +reconcile those generated CRDs! In other words, when using this mode, you will +need to provision additional access for **kro** for every new resource type you +define. + +### Example + +If you want to create a `ResourceGraphDefinition` that specifies a new resource +type with `kind: Foo`, and where the graph includes an `apps/v1/Deployment` and +a `v1/ConfigMap`, you will need to create the following `ClusterRole` to ensure +**kro** has enough access to reconcile your resources: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + rbac.kro.run/aggregate-to-controller: "true" + name: kro:controller:foos +rules: + - apiGroups: + - kro.run + resources: + - foos + verbs: + - "*" + - apiGroups: + - apps + resources: + - deployments + verbs: + - "*" + - apiGroups: + - "" + resources: + - configmaps + verbs: + - "*" +``` diff --git a/website/versioned_docs/version-0.6.3/docs/concepts/_category_.json b/website/versioned_docs/version-0.6.3/docs/concepts/_category_.json new file mode 100644 index 000000000..9645894c5 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/docs/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 40, + "collapsible": true, + "collapsed": false +} diff --git a/website/versioned_docs/version-0.6.3/docs/faq.md b/website/versioned_docs/version-0.6.3/docs/faq.md new file mode 100644 index 000000000..b2b3c68ad --- /dev/null +++ b/website/versioned_docs/version-0.6.3/docs/faq.md @@ -0,0 +1,98 @@ +--- +sidebar_label: "FAQ" +sidebar_position: 100 +--- + +# FAQ + +1. **What is kro?** + + Kube Resource Orchestrator (**kro**) is a new operator for Kubernetes that + simplifies the creation of complex Kubernetes resource configurations. kro + lets you create and manage custom groups of Kubernetes resources by defining + them as a _ResourceGraphDefinition_, the project's fundamental custom resource. + ResourceGraphDefinition specifications define a set of resources and how they relate to + each other functionally. Once defined, resource groups can be applied to a + Kubernetes cluster where the kro controller is running. Once validated by + kro, you can create instances of your resource group. kro translates your + ResourceGraphDefinition instance and its parameters into specific Kubernetes resources + and configurations which it then manages for you. + +2. **How does kro work?** + + kro is designed to use core Kubernetes primitives to make resource grouping, + customization, and dependency management simpler. When a ResourceGraphDefinition is + applied to the cluster, the kro controller verifies its specification, then + dynamically creates a new CRD and registers it with the API server. kro then + deploys a dedicated controller to respond to instance events on the CRD. This + microcontroller is responsible for managing the lifecycle of resources + defined in the ResourceGraphDefinition for each instance that is created. + +3. **How do I use kro?** + + First, you define your custom resource groups by creating _ResourceGraphDefinition_ + specifications. These specify one or more Kubernetes resources, and can + include specific configuration for each resource. + + For example, you can define a _WebApp_ resource group that is composed of a + _Deployment_, pre-configured to deploy your web server backend, and a + _Service_ configured to run on a specific port. You can just as easily create + a more complex _WebAppWithDB_ resource group by combining the existing + _WebApp_ resource group with a _Table_ custom resource to provision a cloud + managed database instance for your web app to use. + + Once you have defined a ResourceGraphDefinition, you can apply it to a Kubernetes + cluster where the kro controller is running. kro will take care of the heavy + lifting of creating CRDs and deploying dedicated controllers in order to + manage instances of your new custom resource group. + + You are now ready to create instances of your new custom resource group, and + kro will respond by dynamically creating, configuring, and managing the + underlying Kubernetes resources for you. + +4. **Why did you build this project?** + + We want to help streamline and simplify building with Kubernetes. Building + with Kubernetes means dealing with resources that need to operate and work + together, and orchestrating this can get complex and difficult at scale. With + this project, we're taking a first step in reducing the complexity of + resource dependency management and customization, paving the way for a simple + and scalable way to create complex custom resources for Kubernetes. + +5. **How do I use KRO resources with ArgoCD?** + + To use KRO resources with ArgoCD, you need to add a specific tracking annotation + to all templated resources in your ResourceGraphDefinition. + + The following code needs to be added to each templated resource: + + ```yaml + metadata: + ownerReferences: + - apiVersion: kro.run/v1alpha1 + kind: ${schema.kind} + name: ${schema.metadata.name} + uid: ${schema.metadata.uid} + blockOwnerDeletion: true + controller: false + annotations: + argocd.argoproj.io/tracking-id: ${schema.metadata.?annotations["argocd.argoproj.io/tracking-id"]} + ``` + + This annotation allows ArgoCD to properly track and manage the resources + created by KRO instances. + + + ![ArgoCD RGD tracked Instance](/img/KRO-ArgoCD-Tracking.png) + + Note that the example shown above depicts ArgoCD's default resource tracking + via annotations. You may choose to use `annotation+label` or just `label` and + if so the example has to be modified to support your configuration. + For more detailed information about ArgoCD resource tracking, please see the + [ArgoCD documentation](https://argo-cd.readthedocs.io/en/stable/user-guide/resource_tracking/). + +6. **Can I use this in production?** + + This project is in active development and not yet intended for production + use. The _ResourceGraphDefinition_ CRD and other APIs used in this project are not + solidified and highly subject to change. diff --git a/website/versioned_docs/version-0.6.3/docs/getting-started/01-Installation.md b/website/versioned_docs/version-0.6.3/docs/getting-started/01-Installation.md new file mode 100644 index 000000000..c86ae215b --- /dev/null +++ b/website/versioned_docs/version-0.6.3/docs/getting-started/01-Installation.md @@ -0,0 +1,110 @@ +--- +sidebar_position: 1 +--- + +# Installing kro + +This guide walks you through the process of installing kro on your Kubernetes +cluster using Helm. + +## Prerequisites + +Before you begin, ensure you have the following: + +1. `Helm` 3.x installed +2. `kubectl` installed and configured to interact with your Kubernetes cluster + +## Installation Steps + +:::info[**Alpha Stage**] + +kro is currently in alpha stage. While the images are publicly available, please +note that the software is still under active development and APIs may change. + +::: + +### Install kro using Helm + +Once authenticated, install kro using the Helm chart: + +Fetch the latest release version from GitHub +```sh +export KRO_VERSION=$(curl -sL \ + https://api.github.com/repos/kubernetes-sigs/kro/releases/latest | \ + jq -r '.tag_name | ltrimstr("v")' + ) +``` +Validate `KRO_VERSION` populated with a version +``` +echo $KRO_VERSION +``` +Install kro using Helm +``` +helm install kro oci://registry.k8s.io/kro/charts/kro \ + --namespace kro \ + --create-namespace \ + --version=${KRO_VERSION} +``` + +## Verifying the Installation + +After running the installation command, verify that Kro has been installed +correctly: + +1. Check the Helm release: + + ```sh + helm -n kro list + ``` + + Expected result: You should see the "kro" release listed. + ``` + NAME NAMESPACE REVISION STATUS + kro kro 1 deployed + ``` + +2. Check the kro pods: + ```sh + kubectl get pods -n kro + ``` + Expected result: You should see kro-related pods running. + ``` + NAME READY STATUS RESTARTS AGE + kro-7d98bc6f46-jvjl5 1/1 Running 0 1s + ``` + +## Upgrading kro + +To upgrade to a newer version of kro, use the Helm upgrade command: + +Replace `` with the version you want to upgrade to. +```bash +export KRO_VERSION= +``` + +Upgrade the controller +``` +helm upgrade kro oci://registry.k8s.io/kro/charts/kro \ + --namespace kro \ + --version=${KRO_VERSION} +``` + +:::info[**CRD Updates**] + +Helm does not support updating CRDs, so you may need to manually update or +remove and reapply kro related CRDs. For more information, refer to the Helm +documentation. + +::: + +## Uninstalling kro + +To uninstall kro, use the following command: + +```bash +helm uninstall kro -n kro +``` + +Keep in mind that this command will remove all kro resources from your cluster, +except for the ResourceGraphDefinition CRD and any other custom resources you may have +created. diff --git a/website/versioned_docs/version-0.6.3/docs/getting-started/02-deploy-a-resource-graph-definition.md b/website/versioned_docs/version-0.6.3/docs/getting-started/02-deploy-a-resource-graph-definition.md new file mode 100644 index 000000000..6ab9eb591 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/docs/getting-started/02-deploy-a-resource-graph-definition.md @@ -0,0 +1,227 @@ +--- +sidebar_position: 2 +--- + +# Deploy Your First ResourceGraphDefinition + +This guide will walk you through creating your first Resource Graph Definition in **kro**. +We'll create a simple `ResourceGraphDefinition` that demonstrates key kro features. + +## What is a **ResourceGraphDefinition**? + +A `ResourceGraphDefinition` lets you create new Kubernetes APIs that deploy multiple +resources together as a single, reusable unit. In this example, we’ll create a +`ResourceGraphDefinition` that packages a reusable set of resources, including a +`Deployment`, `Service`, and `Ingress`. These resources are available in any +Kubernetes cluster. Users can then call the API to deploy resources as a single +unit, ensuring they're always created together with the right configuration. + +Under the hood, when you create a `ResourceGraphDefinition`, kro: + +1. Treats your resources as a Directed Acyclic Graph (DAG) to understand their + dependencies +2. Validates resource definitions and detects the correct deployment order +3. Creates a new API (CRD) in your cluster +4. Configures itself to watch and serve instances of this API + +## Prerequisites + +Before you begin, make sure you have the following: + +- **kro** [installed](./01-Installation.md) and running in your Kubernetes + cluster. +- `kubectl` installed and configured to interact with your Kubernetes cluster. + +## Create your Application ResourceGraphDefinition + +Let's create a Resource Graph Definition that combines a `Deployment`, a `Service` and +`Ingress`. Save this as `resourcegraphdefinition.yaml`: + +```yaml title="resourcegraphdefinition.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: my-application +spec: + # kro uses this simple schema to create your CRD schema and apply it + # The schema defines what users can provide when they instantiate the RGD (create an instance). + schema: + apiVersion: v1alpha1 + kind: Application + spec: + # Spec fields that users can provide. + name: string + image: string | default="nginx" + ingress: + enabled: boolean | default=false + status: + # Fields the controller will inject into instances status. + deploymentConditions: ${deployment.status.conditions} + availableReplicas: ${deployment.status.availableReplicas} + + # Define the resources this API will manage. + resources: + - id: deployment + template: + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ${schema.spec.name} # Use the name provided by user + spec: + replicas: 3 + selector: + matchLabels: + app: ${schema.spec.name} + template: + metadata: + labels: + app: ${schema.spec.name} + spec: + containers: + - name: ${schema.spec.name} + image: ${schema.spec.image} # Use the image provided by user + ports: + - containerPort: 80 + + - id: service + template: + apiVersion: v1 + kind: Service + metadata: + name: ${schema.spec.name}-service + spec: + selector: ${deployment.spec.selector.matchLabels} # Use the deployment selector + ports: + - protocol: TCP + port: 80 + targetPort: 80 + + - id: ingress + includeWhen: + - ${schema.spec.ingress.enabled} # Only include if the user wants to create an Ingress + template: + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: ${schema.spec.name}-ingress + annotations: + kubernetes.io/ingress.class: alb + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/target-type: ip + alb.ingress.kubernetes.io/healthcheck-path: /health + alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]' + alb.ingress.kubernetes.io/target-group-attributes: stickiness.enabled=true,stickiness.lb_cookie.duration_seconds=60 + spec: + rules: + - http: + paths: + - path: "/" + pathType: Prefix + backend: + service: + name: ${service.metadata.name} # Use the service name + port: + number: 80 +``` + +### Deploy the ResourceGraphDefinition + +1. **Create a ResourceGraphDefinition manifest file**: Create a new file with the + `ResourceGraphDefinition` definition. You can use the example above. + +2. **Apply the `ResourceGraphDefinition`**: Use the `kubectl` command to deploy the + ResourceGraphDefinition to your Kubernetes cluster: + + ```bash + kubectl apply -f resourcegraphdefinition.yaml + ``` + +3. **Inspect the `ResourceGraphDefinition`**: Check the status of the resources created by + the ResourceGraphDefinition using the `kubectl` command: + + ```bash + kubectl get rgd my-application -owide + ``` + + You should see the ResourceGraphDefinition in the `Active` state, along with relevant + information to help you understand your application: + + ```bash + NAME APIVERSION KIND STATE TOPOLOGICALORDER AGE + my-application v1alpha1 Application Active ["deployment","service","ingress"] 49 + ``` + +### Create your Application Instance + +Now that your `ResourceGraphDefinition` is created, kro has generated a new API +(Application) that orchestrates the creation of a `Deployment`, a `Service`, and +an `Ingress`. Let's use it! + +1. **Create an Application instance**: Create a new file named `instance.yaml` + with the following content: + + ```yaml title="instance.yaml" + apiVersion: kro.run/v1alpha1 + kind: Application + metadata: + name: my-application-instance + spec: + name: my-awesome-app + ingress: + enabled: true + ``` + +2. **Apply the Application instance**: Use the `kubectl` command to deploy the + Application instance to your Kubernetes cluster: + + ```bash + kubectl apply -f instance.yaml + ``` + +3. **Inspect the Application instance**: Check the status of the resources + + ```bash + kubectl get applications + ``` + + After a few seconds, you should see the Application instance in the `Active` + state: + + ```bash + NAME STATE SYNCED AGE + my-application-instance ACTIVE True 10s + ``` + +4. **Inspect the resources**: Check the resources created by the Application + instance: + + ```bash + kubectl get deployments,services,ingresses + ``` + + You should see the `Deployment`, `Service`, and `Ingress` created by the + Application instance. + + ```bash + NAME READY UP-TO-DATE AVAILABLE AGE + deployment.apps/my-awesome-app 3/3 3 3 69s + + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE + service/my-awesome-app-service ClusterIP 10.100.167.72 80/TCP 65s + + NAME CLASS HOSTS ADDRESS PORTS AGE + ingress.networking.k8s.io/my-awesome-app-ingress * 80 62s + ``` + +### Delete the Application instance + +kro can also help you clean up resources when you're done with them. + +1. **Delete the Application instance**: Clean up the resources by deleting the + Application instance: + + ```bash + kubectl delete application my-application-instance + ``` + + Now, the resources created by the Application instance will be deleted. diff --git a/website/versioned_docs/version-0.6.3/docs/getting-started/_category_.json b/website/versioned_docs/version-0.6.3/docs/getting-started/_category_.json new file mode 100644 index 000000000..723093d16 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/docs/getting-started/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Getting started", + "position": 20, + "collapsible": true, + "collapsed": false +} diff --git a/website/versioned_docs/version-0.6.3/docs/overview.md b/website/versioned_docs/version-0.6.3/docs/overview.md new file mode 100644 index 000000000..6bfe372c3 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/docs/overview.md @@ -0,0 +1,116 @@ +--- +sidebar_position: 1 +--- + +# What is kro? + +**kro** (Kube Resource Orchestrator) is an open-source, Kubernetes-native project +that allows you to define custom **Kubernetes APIs** using simple and straightforward +configuration. With kro, you can easily configure new custom APIs that create a +group of Kubernetes objects and the logical operations between them. kro leverages +[CEL (Common Expression Language)](https://github.com/google/cel-spec), the same +language used by Kubernetes webhooks, for logical operations. Using CEL expressions, +you can easily pass values from one object to another and incorporate conditionals into +your custom API definitions. Based on the CEL expressions, kro automatically calculates +the order in which objects should be created. You can define default values for fields +in the API specification, streamlining the process for end users who can then +effortlessly invoke these custom APIs to create grouped resources. + +# How does kro work? + +### Developer interface + +When the end user applies a YAML spec to the cluster using the **Custom API**, +the API creates a set of resources within the cluster. These resources can +include both **native Kubernetes** resources and any **Custom Resource +Definitions (CRDs)** installed in the cluster. Some of these resources may +create additional resources outside of your cluster. + +As depicted in the following diagram, the Developers call the Custom API, which +creates resources such as the **Deployment**, **Ingress**, **ServiceAccount**, +**Prometheus Monitor**, **IAM Role**, **IAM Policy**, and **Amazon S3 Bucket**. +This allows the Developers to easily manage and deploy their applications in a +standardized and streamlined manner. + +
+ +![End user interface - Custom API](/img/architecture/KRO-Dev-Interface.png) +_Figure 1: End user interface - Custom API_ + +
+ +### ResourceGraphDefinition + +When you install **Kro** in your cluster, it installs a Custom Resource +Definition (CRD) called **ResourceGraphDefinition (RG)**. The **Platform**, **Security**, +and **Compliance** teams can collaborate to create custom APIs by defining +Custom Resources for the ResourceGraphDefinition CRD. + +In the depicted example, the **Platform Team** has created a **RG** with +arbitrary name "Application Stack" that encapsulates the necessary resources, +along with any additional logic, abstractions, and security best practices. When +the RGD is applied to the cluster, a new API of kind ApplicationStack is created +and available for the Developer to interact with. The Developers no longer need to +directly manage the underlying infrastructure complexities, as the custom API +handles the deployment and configuration of the required resources. + +
+ +![Platform Team Interface](/img/architecture/KRO-Platform-Team.png) +_Figure 2: ResourceGraphDefinition (RG) - Platform Team Interface_ + +
+ +### ResourceGraphDefinition Instance + +Developer teams can create multiple instances of the **Application Stack**, each +tailored to their specific requirements. As shown, **Dev Team A** and **Dev Team +B** have both instantiated their own Application Stacks. While the underlying +resources are similar, **Dev Team A** has chosen to expose their service +externally, leveraging the Ingress option, while **Dev Team B** has opted to +keep their service internal to the cluster. This flexibility allows each +development team to customize their application stack based on their specific +requirements. + +
+ +![ResourceGraphDefinition Instance](/img/architecture/KRO-Instance.png) +_Figure 3: ResourceGraphDefinition Instance (RGI)_ + +
+ +# Why kro? + +### Manage any group of resources as one unit + +Using **kro**, the **Platform Team** can enable Developer teams to quickly +deploy and manage applications and their dependencies as one unit, handling the +entire lifecycle from deployment to maintenance. The new APIs integrate +seamlessly with developers' existing CD tools, preserving familiar processes and +interfaces to simplify adoption. + +### Collaborate + +Transform **Kubernetes** into your unified platform configuration framework +using **kro**. Platform, Compliance, and Security teams work together to develop +APIs that standardize and streamline configurations, making it easier for +Developer teams to adopt secure, compliant practices. This collaboration lets +you build your organizational standards directly into the APIs, ensuring every +application deployment aligns with security and compliance requirements without +adding complexity for developers. + +### Standardize + +By creating unified APIs, you can define and enforce best practices across all +environments, ensuring every application meets organizational requirements and +achieving consistency across deployment environments. + +# Community + +We welcome questions, suggestions, and contributions from the community! To get +involved, check out our +[contributing guide](https://github.com/kubernetes-sigs/kro/blob/main/CONTRIBUTING.md). +For bugs or feature requests, feel free to +[submit an issue](https://github.com/kubernetes-sigs/kro/issues). You’re also invited to +join our +[community](https://github.com/kubernetes-sigs/kro?tab=readme-ov-file#community-participation). diff --git a/website/versioned_docs/version-0.6.3/examples/aws/ack-eks-cluster.md b/website/versioned_docs/version-0.6.3/examples/aws/ack-eks-cluster.md new file mode 100644 index 000000000..6de9c7578 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/aws/ack-eks-cluster.md @@ -0,0 +1,222 @@ +--- +sidebar_position: 302 +--- + +# EKS Cluster + +```yaml title="eks.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: ekscluster.kro.run +spec: + # CRD Schema + schema: + apiVersion: v1alpha1 + kind: EKSCluster + spec: + name: string + version: string + status: + networkingInfo: + vpcID: ${clusterVPC.status.vpcID} + subnetAZA: ${clusterSubnetA.status.subnetID} + subnetAZB: ${clusterSubnetB.status.subnetID} + clusterARN: ${cluster.status.ackResourceMetadata.arn} + # resources + resources: + - id: clusterVPC + readyWhen: + - ${clusterVPC.status.state == "available"} + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: VPC + metadata: + name: kro-cluster-vpc + spec: + cidrBlocks: + - 192.168.0.0/16 + enableDNSSupport: true + enableDNSHostnames: true + - id: clusterElasticIPAddress + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: ElasticIPAddress + metadata: + name: kro-cluster-eip + spec: {} + - id: clusterInternetGateway + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: InternetGateway + metadata: + name: kro-cluster-igw + spec: + vpc: ${clusterVPC.status.vpcID} + - id: clusterRouteTable + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: RouteTable + metadata: + name: kro-cluster-public-route-table + spec: + vpcID: ${clusterVPC.status.vpcID} + routes: + - destinationCIDRBlock: 0.0.0.0/0 + gatewayID: ${clusterInternetGateway.status.internetGatewayID} + - id: clusterSubnetA + readyWhen: + - ${clusterSubnetA.status.state == "available"} + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: Subnet + metadata: + name: kro-cluster-public-subnet1 + spec: + availabilityZone: us-west-2a + cidrBlock: 192.168.0.0/18 + vpcID: ${clusterVPC.status.vpcID} + routeTables: + - ${clusterRouteTable.status.routeTableID} + mapPublicIPOnLaunch: true + - id: clusterSubnetB + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: Subnet + metadata: + name: kro-cluster-public-subnet2 + spec: + availabilityZone: us-west-2b + cidrBlock: 192.168.64.0/18 + vpcID: ${clusterVPC.status.vpcID} + routeTables: + - ${clusterRouteTable.status.routeTableID} + mapPublicIPOnLaunch: true + - id: clusterNATGateway + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: NATGateway + metadata: + name: kro-cluster-natgateway1 + spec: + subnetID: ${clusterSubnetB.status.subnetID} + allocationID: ${clusterElasticIPAddress.status.allocationID} + - id: clusterRole + template: + apiVersion: iam.services.k8s.aws/v1alpha1 + kind: Role + metadata: + name: kro-cluster-role + spec: + name: kro-cluster-role + description: "kro created cluster cluster role" + policies: + - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy + assumeRolePolicyDocument: | + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "eks.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] + } + - id: clusterNodeRole + template: + apiVersion: iam.services.k8s.aws/v1alpha1 + kind: Role + metadata: + name: kro-cluster-node-role + spec: + name: kro-cluster-node-role + description: "kro created cluster node role" + policies: + - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy + - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly + - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy + assumeRolePolicyDocument: | + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] + } + - id: clusterAdminRole + template: + apiVersion: iam.services.k8s.aws/v1alpha1 + kind: Role + metadata: + name: kro-cluster-pia-role + spec: + name: kro-cluster-pia-role + description: "kro created cluster admin pia role" + policies: + - arn:aws:iam::aws:policy/AdministratorAccess + assumeRolePolicyDocument: | + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowEksAuthToAssumeRoleForPodIdentity", + "Effect": "Allow", + "Principal": { + "Service": "pods.eks.amazonaws.com" + }, + "Action": [ + "sts:AssumeRole", + "sts:TagSession" + ] + } + ] + } + - id: cluster + readyWhen: + - ${cluster.status.status == "ACTIVE"} + template: + apiVersion: eks.services.k8s.aws/v1alpha1 + kind: Cluster + metadata: + name: ${schema.spec.name} + spec: + name: ${schema.spec.name} + accessConfig: + authenticationMode: API_AND_CONFIG_MAP + roleARN: ${clusterRole.status.ackResourceMetadata.arn} + version: ${schema.spec.version} + resourcesVPCConfig: + endpointPrivateAccess: false + endpointPublicAccess: true + subnetIDs: + - ${clusterSubnetA.status.subnetID} + - ${clusterSubnetB.status.subnetID} + - id: clusterNodeGroup + template: + apiVersion: eks.services.k8s.aws/v1alpha1 + kind: Nodegroup + metadata: + name: kro-cluster-nodegroup + spec: + name: kro-cluster-ng + diskSize: 100 + clusterName: ${cluster.spec.name} + subnets: + - ${clusterSubnetA.status.subnetID} + - ${clusterSubnetB.status.subnetID} + nodeRole: ${clusterNodeRole.status.ackResourceMetadata.arn} + updateConfig: + maxUnavailable: 1 + scalingConfig: + minSize: 1 + maxSize: 1 + desiredSize: 1 +``` diff --git a/website/versioned_docs/version-0.6.3/examples/aws/ack-networking-stack.md b/website/versioned_docs/version-0.6.3/examples/aws/ack-networking-stack.md new file mode 100644 index 000000000..f38817180 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/aws/ack-networking-stack.md @@ -0,0 +1,79 @@ +--- +sidebar_position: 303 +--- + +# Networking Stack + +```yaml title="networking-stack.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: networkingstack.kro.run +spec: + # CRD Schema + schema: + apiVersion: v1alpha1 + kind: NetworkingStack + spec: + name: string + status: + networkingInfo: + vpcID: ${vpc.status.vpcID} + subnetAZA: ${subnetAZA.status.subnetID} + subnetAZB: ${subnetAZB.status.subnetID} + subnetAZC: ${subnetAZC.status.subnetID} + securityGroup: ${securityGroup.status.id} + # resources + resources: + - id: vpc + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: VPC + metadata: + name: vpc-${schema.spec.name} + spec: + cidrBlocks: + - 192.168.0.0/16 + enableDNSHostnames: false + enableDNSSupport: true + - id: subnetAZA + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: Subnet + metadata: + name: subnet-a-${schema.spec.name} + spec: + availabilityZone: us-west-2a + cidrBlock: 192.168.0.0/18 + vpcID: ${vpc.status.vpcID} + - id: subnetAZB + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: Subnet + metadata: + name: subnet-b-${schema.spec.name} + spec: + availabilityZone: us-west-2b + cidrBlock: 192.168.64.0/18 + vpcID: ${vpc.status.vpcID} + - id: subnetAZC + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: Subnet + metadata: + name: subnet-c-${schema.spec.name} + spec: + availabilityZone: us-west-2c + cidrBlock: 192.168.128.0/18 + vpcID: ${vpc.status.vpcID} + - id: securityGroup + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: SecurityGroup + metadata: + name: cluster-security-group-${schema.spec.name} + spec: + vpcID: ${vpc.status.vpcID} + name: my-sg-${schema.spec.name} + description: something something +``` diff --git a/website/versioned_docs/version-0.6.3/examples/aws/ack-valkey-cachecluster.md b/website/versioned_docs/version-0.6.3/examples/aws/ack-valkey-cachecluster.md new file mode 100644 index 000000000..46c0b537a --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/aws/ack-valkey-cachecluster.md @@ -0,0 +1,76 @@ +--- +sidebar_position: 304 +--- + +# Valkey cluster + +```yaml title="valkey-cachecluster.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: valkey.kro.run +spec: + schema: + apiVersion: v1alpha1 + kind: Valkey + spec: + name: string + status: + csgARN: ${cacheSubnetGroup.status.ackResourceMetadata.arn} + subnets: ${cacheSubnetGroup.status.subnets} + clusterARN: ${valkey.status.ackResourceMetadata.arn} + resources: + - id: networkingStack + template: + apiVersion: kro.run/v1alpha1 + kind: NetworkingStack + metadata: + name: ${schema.spec.name}-networking-stack + spec: + name: ${schema.spec.name}-networking-stack + - id: cacheSubnetGroup + template: + apiVersion: elasticache.services.k8s.aws/v1alpha1 + kind: CacheSubnetGroup + metadata: + name: ${schema.spec.name}-valkey-subnet-group + spec: + cacheSubnetGroupDescription: "Valkey ElastiCache subnet group" + cacheSubnetGroupName: ${schema.spec.name}-valkey-subnet-group + subnetIDs: + - ${networkingStack.status.networkingInfo.subnetAZA} + - ${networkingStack.status.networkingInfo.subnetAZB} + - ${networkingStack.status.networkingInfo.subnetAZC} + - id: sg + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: SecurityGroup + metadata: + name: ${schema.spec.name}-valkey-sg + spec: + name: ${schema.spec.name}-valkey-sg + description: "Valkey ElastiCache security group" + vpcID: ${networkingStack.status.networkingInfo.vpcID} + ingressRules: + - fromPort: 6379 + toPort: 6379 + ipProtocol: tcp + ipRanges: + - cidrIP: 0.0.0.0/0 + - id: valkey + template: + apiVersion: elasticache.services.k8s.aws/v1alpha1 + kind: CacheCluster + metadata: + name: ${schema.spec.name}-valkey + spec: + cacheClusterID: vote-valkey-cluster + cacheNodeType: cache.t3.micro + cacheSubnetGroupName: ${schema.spec.name}-valkey-subnet-group + engine: valkey + engineVersion: "8.x" + numCacheNodes: 1 + port: 6379 + securityGroupIDs: + - ${sg.status.id} +``` diff --git a/website/versioned_docs/version-0.6.3/examples/aws/aws-examples.md b/website/versioned_docs/version-0.6.3/examples/aws/aws-examples.md new file mode 100644 index 000000000..71c170923 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/aws/aws-examples.md @@ -0,0 +1,28 @@ +--- +sidebar_position: 301 +--- + +:::info[**ACK controllers Dependency**] +Most examples below will need ACK controllers to be running in the cluster either through kro or other means. +::: + +# AWS Examples +- [Deploying a Controller](./deploying-controller.md) Discover how to deploy a + Kubernetes controller using kro ResourceGraphDefinitions, including the necessary + Deployment, ServiceAccount, and CRDs. + +- [AWS Networking Stack](./ack-networking-stack.md) Learn how to define and + manage an AWS networking stack using kro ResourceGraphDefinitions, including VPCs, + subnets, and security groups. + +- [EKS Cluster with ACK CRDs](./ack-eks-cluster.md) Explore how to define and + manage an EKS cluster using AWS Controllers for Kubernetes (ACK) CRDs within a + kro ResourceGraphDefinition. + +- [Valkey CacheCluster with ACK CRDs](./ack-valkey-cachecluster.md) Learn how to + create and configure a Valkey CacheCluster using ACK CRDs in a kro + ResourceGraphDefinition. + +- [Pod and RDS DBInstance](./pod-rds-dbinstance.md) Deploy a Pod and an RDS + DBInstance in a kro ResourceGraphDefinition, showcasing the use of multiple resources + with dependencies. diff --git a/website/versioned_docs/version-0.6.3/examples/aws/category.json b/website/versioned_docs/version-0.6.3/examples/aws/category.json new file mode 100644 index 000000000..ce984a932 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/aws/category.json @@ -0,0 +1,10 @@ +{ + "label": "AWS Examples", + "position": 300, + "link": { + "type": "generated-index", + "description": "Examples of using Kro with AWS" + }, + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-0.6.3/examples/aws/deploying-controller.md b/website/versioned_docs/version-0.6.3/examples/aws/deploying-controller.md new file mode 100644 index 000000000..ddb999bd6 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/aws/deploying-controller.md @@ -0,0 +1,297 @@ +--- +sidebar_position: 305 +--- + +# Controller Deployment + +```yaml title="controller-rg.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: ekscontrollers.kro.run +spec: + schema: + apiVersion: v1alpha1 + kind: EKSController + spec: + name: string | default=eks-controller + namespace: string | default=default + values: + aws: + accountID: string | required=true + region: string | default=us-west-2 + deployment: + containerPort: integer | default=8080 + replicas: integer | default=1 + iamRole: + maxSessionDuration: integer | default=3600 + oidcProvider: string | required=true + roleDescription: string | default=IRSA role for ACK EKS controller deployment on EKS cluster using kro Resource group + iamPolicy: + # would prefer to add a policyDocument here, need to support multiline string here + description: string | default="policy for eks controller" + image: + deletePolicy: string | default=delete + repository: string | default=public.ecr.aws/aws-controllers-k8s/eks-controller + tag: string | default=1.4.7 + resources: + requests: + memory: string | default=64Mi + cpu: string | default=50m + limits: + memory: string | default=128Mi + cpu: string | default=100m + log: + enabled: boolean | default=false + level: string | default=info + serviceAccount: + name: string | default=eks-controller-sa + resources: + - id: eksCRDGroup + template: + apiVersion: kro.run/v1alpha1 + kind: EKSCRDGroup + metadata: + name: ${schema.spec.name}-crd-group + spec: + name: ${schema.spec.name}-crd-group + - id: eksControllerIamPolicy + template: + apiVersion: iam.services.k8s.aws/v1alpha1 + kind: Policy + metadata: + name: ${schema.spec.name}-iam-policy + spec: + name: ${schema.spec.name}-iam-policy + description: ${schema.spec.values.iamPolicy.description} + policyDocument: > + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": [ + "eks:*", + "iam:GetRole", + "iam:PassRole", + "iam:ListAttachedRolePolicies", + "ec2:DescribeSubnets" + ], + "Resource": "*" + } + ] + } + - id: eksControllerIamRole + template: + apiVersion: iam.services.k8s.aws/v1alpha1 + kind: Role + metadata: + name: ${schema.spec.name}-iam-role + namespace: ${schema.spec.namespace} + spec: + name: ${schema.spec.name}-iam-role + description: ${schema.spec.values.iamRole.roleDescription} + maxSessionDuration: ${schema.spec.values.iamRole.maxSessionDuration} + policies: + - ${eksControllerIamPolicy.status.ackResourceMetadata.arn} + assumeRolePolicyDocument: > + { + "Version":"2012-10-17", + "Statement": [{ + "Effect":"Allow", + "Principal": {"Federated": "arn:aws:iam::${schema.spec.values.aws.accountID}:oidc-provider/${schema.spec.values.iamRole.oidcProvider}"}, + "Action": ["sts:AssumeRoleWithWebIdentity"], + "Condition": { + "StringEquals": {"${schema.spec.values.iamRole.oidcProvider}:sub": "system:serviceaccount:${schema.spec.namespace}:${schema.spec.values.serviceAccount.name}"} + } + }] + } + - id: serviceAccount + template: + apiVersion: v1 + kind: ServiceAccount + metadata: + name: ${schema.spec.values.serviceAccount.name} + namespace: ${schema.spec.namespace} + annotations: + eks.amazonaws.com/role-arn : ${eksControllerIamRole.status.ackResourceMetadata.arn} + - id: deployment + template: + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ${schema.spec.name}-deployment + namespace: ${schema.spec.namespace} + labels: + app.kubernetes.io.name: ${schema.spec.name}-deployment + app.kubernetes.io.instance: ${schema.spec.name} + spec: + replicas: ${schema.spec.values.deployment.replicas} + selector: + matchLabels: + app.kubernetes.io.name: ${schema.spec.name}-deployment + app.kubernetes.io.instance: ${schema.spec.name} + template: + metadata: + labels: + app.kubernetes.io.name: ${schema.spec.name}-deployment + app.kubernetes.io.instance: ${schema.spec.name} + spec: + serviceAccountName: ${serviceAccount.metadata.name} + containers: + - command: + - ./bin/controller + args: + - --aws-region + - ${schema.spec.values.aws.region} + - --enable-development-logging=${schema.spec.values.log.enabled} + - --log-level + - ${schema.spec.values.log.level} + - --deletion-policy + - ${schema.spec.values.image.deletePolicy} + - --watch-namespace + - ${schema.spec.namespace} + image: ${schema.spec.values.image.repository}:${schema.spec.values.image.tag} + name: controller + ports: + - name: http + containerPort: ${schema.spec.values.deployment.containerPort} + resources: + requests: + memory: ${schema.spec.values.image.resources.requests.memory} + cpu: ${schema.spec.values.image.resources.requests.cpu} + limits: + memory: ${schema.spec.values.image.resources.limits.memory} + cpu: ${schema.spec.values.image.resources.limits.cpu} + env: + - name: ACK_SYSTEM_NAMESPACE + value: ${schema.spec.namespace} + - name: AWS_REGION + value: ${schema.spec.values.aws.region} + - name: DELETE_POLICY + value: ${schema.spec.values.image.deletePolicy} + - name: ACK_LOG_LEVEL + value: ${schema.spec.values.log.level} + ports: + - containerPort: 80 + - id: clusterRoleBinding + template: + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: ${schema.spec.name}-clusterrolebinding + roleRef: + kind: ClusterRole + apiGroup: rbac.authorization.k8s.io + name: ${clusterRole.metadata.name} + subjects: + - kind: ServiceAccount + name: ${serviceAccount.metadata.name} + namespace: ${serviceAccount.metadata.namespace} + - id: clusterRole + template: + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: ${schema.spec.name}-clusterrole + rules: + - apiGroups: + - "" + resources: + - configmaps + - secrets + verbs: + - get + - list + - patch + - watch + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch + - apiGroups: + - ec2.services.k8s.aws + resources: + - securitygroups + - securitygroups/status + - subnets + - subnets/status + verbs: + - get + - list + - apiGroups: + - eks.services.k8s.aws + resources: + - accessentries + - addons + - clusters + - fargateprofiles + - identityproviderconfigs + - nodegroups + - podidentityassociations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - eks.services.k8s.aws + resources: + - accessentries/status + - addons/status + - clusters/status + - fargateprofiles/status + - identityproviderconfigs/status + - nodegroups/status + - podidentityassociations/status + verbs: + - get + - patch + - update + - apiGroups: + - iam.services.k8s.aws + resources: + - roles + - roles/status + verbs: + - get + - list + - apiGroups: + - kms.services.k8s.aws + resources: + - keys + - keys/status + verbs: + - get + - list + - apiGroups: + - services.k8s.aws + resources: + - adoptedresources + - fieldexports + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - services.k8s.aws + resources: + - adoptedresources/status + - fieldexports/status + verbs: + - get + - patch + - update +``` diff --git a/website/versioned_docs/version-0.6.3/examples/aws/pod-rds-dbinstance.md b/website/versioned_docs/version-0.6.3/examples/aws/pod-rds-dbinstance.md new file mode 100644 index 000000000..9f0ca72c5 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/aws/pod-rds-dbinstance.md @@ -0,0 +1,50 @@ +--- +sidebar_position: 306 +--- + +# Pod with RDS DBInstance + +```yaml title="deploymentdbinstance-rg.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: deploymentandawspostgres +spec: + # CRD Definition + schema: + apiVersion: v1alpha1 + kind: DeploymentAndAWSPostgres + spec: + applicationName: string + image: string + location: string + + # Resources + resources: + - id: dbinstance + template: + apiVersion: rds.services.k8s.aws/v1alpha1 + kind: DBInstance + metadata: + name: ${schema.spec.applicationName}-dbinstance + spec: + # need to specify the required fields (e.g masterUsername, masterPassword) + engine: postgres + dbInstanceIdentifier: ${schema.spec.applicationName}-dbinstance + allocatedStorage: 20 + dbInstanceClass: db.t3.micro + + - id: pod + template: + apiVersion: v1 + kind: Pod + metadata: + name: ${schema.spec.applicationName}-pod + spec: + containers: + - name: container1 + image: ${schema.spec.image} + env: + - name: POSTGRES_ENDPOINT + value: ${dbinstance.status.endpoint.address} +``` diff --git a/website/versioned_docs/version-0.6.3/examples/azure/category.json b/website/versioned_docs/version-0.6.3/examples/azure/category.json new file mode 100644 index 000000000..2298240e4 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/azure/category.json @@ -0,0 +1,10 @@ +{ + "label": "Azure Examples", + "position": 500, + "link": { + "type": "generated-index", + "description": "Examples of using Kro with Microsoft Azure" + }, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/website/versioned_docs/version-0.6.3/examples/basic/category.json b/website/versioned_docs/version-0.6.3/examples/basic/category.json new file mode 100644 index 000000000..02167280d --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/basic/category.json @@ -0,0 +1,10 @@ +{ + "label": "Basic Examples", + "position": 100, + "link": { + "type": "generated-index", + "description": "Basic examples to get started with Kro" + }, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/website/versioned_docs/version-0.6.3/examples/basic/noop.md b/website/versioned_docs/version-0.6.3/examples/basic/noop.md new file mode 100644 index 000000000..3258bcbdd --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/basic/noop.md @@ -0,0 +1,19 @@ +--- +sidebar_position: 101 +--- + +# Empty ResourceGraphDefinition + +```yaml title="noop.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: noop +spec: + schema: + apiVersion: v1alpha1 + kind: NoOp + spec: + name: string | required=true + resources: [] +``` diff --git a/website/versioned_docs/version-0.6.3/examples/basic/optionals.md b/website/versioned_docs/version-0.6.3/examples/basic/optionals.md new file mode 100644 index 000000000..673063621 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/basic/optionals.md @@ -0,0 +1,60 @@ +--- +sidebar_position: 104 +--- + +# Optional Values & External References + + +```yaml title="config-map.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: demo +data: + ECHO_VALUE: "Hello, World!" +``` + + +```yaml title="deploymentservice-rg.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: deploymentservice +spec: + schema: + apiVersion: v1alpha1 + kind: DeploymentService + spec: + name: string + resources: + - id: input + externalRef: + apiVersion: v1 + kind: ConfigMap + metadata: + name: demo + namespace: default + - id: deployment + template: + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ${schema.spec.name} + spec: + replicas: 1 + selector: + matchLabels: + app: deployment + template: + metadata: + labels: + app: deployment + spec: + containers: + - name: ${schema.spec.name}-busybox + image: busybox + command: ["sh", "-c", "echo $MY_VALUE && sleep 3600"] + env: + - name: MY_VALUE + value: ${input.data.?ECHO_VALUE} +``` \ No newline at end of file diff --git a/website/versioned_docs/version-0.6.3/examples/basic/web-app-ingress.md b/website/versioned_docs/version-0.6.3/examples/basic/web-app-ingress.md new file mode 100644 index 000000000..2767fca30 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/basic/web-app-ingress.md @@ -0,0 +1,92 @@ +--- +sidebar_position: 102 +--- + +# Web Application w/ Ingress + +```yaml title="webapp-ingress.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: my-application +spec: + # kro uses this simple schema to create your CRD schema and apply it + # The schema defines what users can provide when they instantiate the RGD (create an instance). + schema: + apiVersion: v1alpha1 + kind: Application + spec: + # Spec fields that users can provide. + name: string + image: string | default="nginx" + ingress: + enabled: boolean | default=false + status: + # Fields the controller will inject into instances status. + deploymentConditions: ${deployment.status.conditions} + availableReplicas: ${deployment.status.availableReplicas} + + # Define the resources this API will manage. + resources: + - id: deployment + template: + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ${schema.spec.name} # Use the name provided by user + spec: + replicas: 3 + selector: + matchLabels: + app: ${schema.spec.name} + template: + metadata: + labels: + app: ${schema.spec.name} + spec: + containers: + - name: ${schema.spec.name} + image: ${schema.spec.image} # Use the image provided by user + ports: + - containerPort: 80 + + - id: service + template: + apiVersion: v1 + kind: Service + metadata: + name: ${schema.spec.name}-service + spec: + selector: ${deployment.spec.selector.matchLabels} # Use the deployment selector + ports: + - protocol: TCP + port: 80 + targetPort: 80 + + - id: ingress + includeWhen: + - ${schema.spec.ingress.enabled} # Only include if the user wants to create an Ingress + template: + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: ${schema.spec.name}-ingress + annotations: + kubernetes.io/ingress.class: alb + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/target-type: ip + alb.ingress.kubernetes.io/healthcheck-path: /health + alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]' + alb.ingress.kubernetes.io/target-group-attributes: stickiness.enabled=true,stickiness.lb_cookie.duration_seconds=60 + spec: + rules: + - http: + paths: + - path: "/" + pathType: Prefix + backend: + service: + name: ${service.metadata.name} # Use the service name + port: + number: 80 +``` diff --git a/website/versioned_docs/version-0.6.3/examples/basic/web-app.md b/website/versioned_docs/version-0.6.3/examples/basic/web-app.md new file mode 100644 index 000000000..6619310ec --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/basic/web-app.md @@ -0,0 +1,53 @@ +--- +sidebar_position: 103 +--- + +# Web Application + +```yaml title="deploymentservice-rg.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: deploymentservice +spec: + schema: + apiVersion: v1alpha1 + kind: DeploymentService + spec: + name: string + resources: + - id: deployment + template: + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ${schema.spec.name} + spec: + replicas: 1 + selector: + matchLabels: + app: deployment + template: + metadata: + labels: + app: deployment + spec: + containers: + - name: ${schema.spec.name}-deployment + image: nginx + ports: + - containerPort: 80 + - id: service + template: + apiVersion: v1 + kind: Service + metadata: + name: ${schema.spec.name} + spec: + selector: + app: deployment + ports: + - protocol: TCP + port: 80 + targetPort: 80 +``` diff --git a/website/versioned_docs/version-0.6.3/examples/examples.md b/website/versioned_docs/version-0.6.3/examples/examples.md new file mode 100644 index 000000000..e1dcadcb6 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/examples.md @@ -0,0 +1,29 @@ +--- +sidebar_position: 0 +--- + +# Examples + +This section provides a collection of examples demonstrating how to define and +use ResourceGraphDefinitions in **kro** for various scenarios. Each example showcases a +specific use case and includes a detailed explanation along with the +corresponding YAML definitions. + +## Basic Examples + +- [Empty ResourceGraphDefinition (Noop)](./basic/noop.md) Explore the simplest form of a + ResourceGraphDefinition that doesn't define any resources, serving as a reference for + the basic structure. + +- [Simple Web Application](./basic/web-app.md) Deploy a basic web application with a + Deployment and Service. + +- [Web Application with Ingress](./basic/web-app-ingress.md) Extend the basic web + application example to include an optional Ingress resource for external + access. + +## Advanced Examples + +- [Deploying CoreDNS](./kubernetes/deploying-coredns.md) Learn how to deploy CoreDNS in a + Kubernetes cluster using kro ResourceGraphDefinitions, including the necessary + Deployment, Service, and ConfigMap. diff --git a/website/versioned_docs/version-0.6.3/examples/gcp/category.json b/website/versioned_docs/version-0.6.3/examples/gcp/category.json new file mode 100644 index 000000000..4277df418 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/gcp/category.json @@ -0,0 +1,10 @@ +{ + "label": "GCP Examples", + "position": 400, + "link": { + "type": "generated-index", + "description": "Examples of using Kro with Google Cloud Platform" + }, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/website/versioned_docs/version-0.6.3/examples/gcp/cloud-sql.md b/website/versioned_docs/version-0.6.3/examples/gcp/cloud-sql.md new file mode 100644 index 000000000..c3eb689dd --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/gcp/cloud-sql.md @@ -0,0 +1,274 @@ +--- +sidebar_position: 406 +--- + +# CloudSQL + +This example show how you can use KRO to deploy GCP Cloud SQL instance in 2 regions as a primary and replica instances. + + +## End User: CloudSQL +The administrator needs to install the RGD first. +The end user creates a `CloudSQL` resource that looks like this: + +```yaml +apiVersion: kro.run/v1alpha1 +kind: CloudSQL +metadata: + name: demo + namespace: config-connector +spec: + name: demo + project: my-gcp-project + primaryRegion: us-central1 + replicaRegion: us-west1 +``` + +The status of the applied resource can be checked using: + +``` +kubectl get cloudsqls +kubectl get cloudsql demo -n config-connector -o yaml +``` + +Navigate to CloudSQL page in the GCP Console and verify the creation of primary and replica instances. + +Once done, the user can delete the `CloudSQL` instance: + +``` +kubectl delete cloudsql demo -n config-connector +``` + +## Administrator: ResourceGraphDefinition +The administrator needs to install the RGD in the cluster first before the user can consume it: + +``` +kubectl apply -f rgd.yaml +``` + +Validate the RGD is installed correctly: + +``` +> kubectl get rgd cloudsql.kro.run +NAME APIVERSION KIND STATE AGE +cloudsql.kro.run v1alpha1 CloudSQL Active 44m +``` + +Once all user created instances are deleted, the administrator can choose to deleted the RGD. + +
+ ResourceGraphDefinition + ```yaml title="rgd.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: cloudsql.kro.run +spec: + schema: + apiVersion: v1alpha1 + kind: CloudSQL + spec: + name: string + project: string + primaryRegion: string + replicaRegion: string + status: + connectionName: ${sqlPrimary.status.connectionName} + ipAddress: ${sqlPrimary.status.firstIpAddress} + resources: + - id: cloudkmsEnable + template: + apiVersion: serviceusage.cnrm.cloud.google.com/v1beta1 + kind: Service + metadata: + annotations: + cnrm.cloud.google.com/deletion-policy: "abandon" + cnrm.cloud.google.com/disable-dependent-services: "false" + name: cloudkms-enablement + spec: + resourceID: cloudkms.googleapis.com + - id: iamEnable + template: + apiVersion: serviceusage.cnrm.cloud.google.com/v1beta1 + kind: Service + metadata: + annotations: + cnrm.cloud.google.com/deletion-policy: "abandon" + cnrm.cloud.google.com/disable-dependent-services: "false" + name: iam-enablement + spec: + resourceID: iam.googleapis.com + - id: serviceUsageEnable + template: + apiVersion: serviceusage.cnrm.cloud.google.com/v1beta1 + kind: Service + metadata: + annotations: + cnrm.cloud.google.com/deletion-policy: "abandon" + cnrm.cloud.google.com/disable-dependent-services: "false" + name: serviceusage-enablement + spec: + resourceID: serviceusage.googleapis.com + - id: sqlAdminEnable + template: + apiVersion: serviceusage.cnrm.cloud.google.com/v1beta1 + kind: Service + metadata: + annotations: + cnrm.cloud.google.com/deletion-policy: "abandon" + cnrm.cloud.google.com/disable-dependent-services: "false" + name: sqladmin-enablement + spec: + resourceID: sqladmin.googleapis.com + - id: serviceidentity + template: + apiVersion: serviceusage.cnrm.cloud.google.com/v1beta1 + kind: ServiceIdentity + metadata: + labels: + enabled-service: ${serviceUsageEnable.metadata.name} + name: sqladmin.googleapis.com + spec: + projectRef: + external: ${schema.spec.project} + - id: keyringPrimary + template: + apiVersion: kms.cnrm.cloud.google.com/v1beta1 + kind: KMSKeyRing + metadata: + labels: + enabled-service: ${cloudkmsEnable.metadata.name} + name: ${schema.spec.name}-primary + spec: + location: ${schema.spec.primaryRegion} + - id: keyringReplica + template: + apiVersion: kms.cnrm.cloud.google.com/v1beta1 + kind: KMSKeyRing + metadata: + labels: + enabled-service: ${cloudkmsEnable.metadata.name} + name: ${schema.spec.name}-replica + spec: + location: ${schema.spec.replicaRegion} + - id: kmskeyPrimary + template: + apiVersion: kms.cnrm.cloud.google.com/v1beta1 + kind: KMSCryptoKey + metadata: + labels: + enabled-service: ${cloudkmsEnable.metadata.name} + failure-zone: ${schema.spec.primaryRegion} + name: ${schema.spec.name}-primary + spec: + keyRingRef: + name: ${keyringPrimary.metadata.name} + #namespace: {{ cloudsqls.metadata.namespace }} + purpose: ENCRYPT_DECRYPT + versionTemplate: + algorithm: GOOGLE_SYMMETRIC_ENCRYPTION + protectionLevel: SOFTWARE + importOnly: false + - id: kmskeyReplica + template: + apiVersion: kms.cnrm.cloud.google.com/v1beta1 + kind: KMSCryptoKey + metadata: + labels: + enabled-service: ${cloudkmsEnable.metadata.name} + failure-zone: ${schema.spec.replicaRegion} + name: ${schema.spec.name}-replica + spec: + keyRingRef: + name: ${keyringReplica.metadata.name} + #namespace: {{ cloudsqls.metadata.namespace }} + purpose: ENCRYPT_DECRYPT + versionTemplate: + algorithm: GOOGLE_SYMMETRIC_ENCRYPTION + protectionLevel: SOFTWARE + importOnly: false + - id: iampolicymemberPrimary + template: + apiVersion: iam.cnrm.cloud.google.com/v1beta1 + kind: IAMPolicyMember + metadata: + labels: + enabled-service: ${iamEnable.metadata.name} + name: sql-kms-${schema.spec.primaryRegion}-policybinding + spec: + member: serviceAccount:${serviceidentity.status.email} + role: roles/cloudkms.cryptoKeyEncrypterDecrypter + resourceRef: + kind: KMSCryptoKey + name: ${kmskeyPrimary.metadata.name}-primary + #namespace: {{ cloudsqls.metadata.namespace }} + - id: iampolicymemberReplica + template: + apiVersion: iam.cnrm.cloud.google.com/v1beta1 + kind: IAMPolicyMember + metadata: + name: sql-kms-${schema.spec.replicaRegion}-policybinding + labels: + enabled-service: ${iamEnable.metadata.name} + spec: + member: serviceAccount:${serviceidentity.status.email} + role: roles/cloudkms.cryptoKeyEncrypterDecrypter + resourceRef: + kind: KMSCryptoKey + name: ${kmskeyReplica.metadata.name}-replica + #namespace: {{ cloudsqls.metadata.namespace }} + - id: sqlPrimary + template: + apiVersion: sql.cnrm.cloud.google.com/v1beta1 + kind: SQLInstance + metadata: + annotations: + cnrm.cloud.google.com/deletion-policy: abandon + labels: + failure-zone: ${schema.spec.primaryRegion} + enabled-service: ${sqlAdminEnable.metadata.name} + name: ${schema.spec.name}-primary + spec: + databaseVersion: POSTGRES_13 + encryptionKMSCryptoKeyRef: + external: projects/${schema.spec.project}/locations/${schema.spec.primaryRegion}/keyRings/${keyringPrimary.metadata.name}/cryptoKeys/${kmskeyPrimary.metadata.name} + region: ${schema.spec.primaryRegion} + settings: + availabilityType: REGIONAL + backupConfiguration: + backupRetentionSettings: + retainedBackups: 6 + enabled: true + location: us + diskSize: 50 + diskType: PD_SSD + maintenanceWindow: + day: 7 + hour: 3 + tier: db-custom-8-30720 + - id: sqlReplica + template: + apiVersion: sql.cnrm.cloud.google.com/v1beta1 + kind: SQLInstance + metadata: + annotations: + cnrm.cloud.google.com/deletion-policy: abandon + labels: + failure-zone: ${schema.spec.replicaRegion} + enabled-service: ${sqlAdminEnable.metadata.name} + name: ${schema.spec.name}-replica + spec: + databaseVersion: POSTGRES_13 + encryptionKMSCryptoKeyRef: + external: projects/${schema.spec.project}/locations/${schema.spec.replicaRegion}/keyRings/${keyringReplica.metadata.name}/cryptoKeys/${kmskeyReplica.metadata.name} + masterInstanceRef: + name: ${schema.spec.name}-primary + #namespace: {{ cloudsqls.metadata.namespace }} + region: ${schema.spec.replicaRegion} + settings: + availabilityType: REGIONAL + diskSize: 50 + diskType: PD_SSD + tier: db-custom-8-30720 + ``` +
diff --git a/website/versioned_docs/version-0.6.3/examples/gcp/eventarc.md b/website/versioned_docs/version-0.6.3/examples/gcp/eventarc.md new file mode 100644 index 000000000..5ff95175b --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/gcp/eventarc.md @@ -0,0 +1,273 @@ +--- +sidebar_position: 407 +--- + +# GCSBucketWithFinalizerTrigger + +A **Platform Administrator** wants to give end users in their organization self-service access to creating GCS Buckets that triggers a Cloud Workflow when any object in it is finalized. The platform administrator creates a kro ResourceGraphDefinition called *gcsbucketwithfinalizertrigger.kro.run* that defines the required Kubernetes resources and a CRD called *GCSBucketWithFinalizertrigger* that exposes only the options they want to be configurable by end users. + +The following KCC objects are created by this RGD: +* IAMServiceAccount, IAMPolicyMember: Service Account with necessary permissions for Eventarc and Pub/Sub. +* StorageBucket +* PubSubTopic +* EventArcTrigger +* StorageNotification: To publish events from the GCS bucket to a Pub/Sub topic. + +Pre-requisites: +* Workflow: The workflow to be triggered on Finalizer event. + +Everything related to these resources would be hidden from the end user, simplifying their experience. + +![GCS EventArc Stack](gcsbucket-with-finalizer-trigger.png) + + + + + +## End User: GCSBucketWithFinalizerTrigger + +The administrator needs to install the RGD first. +The end user creates a `GCSBucketWithFinalizerTrigger` resource something like this: + +```yaml +apiVersion: kro.run/v1alpha1 +kind: GCSBucketWithFinalizerTrigger +metadata: + name: gcsevent-test + namespace: config-connector +spec: + name: demo-gcs # used as name or prefix for KCC objects + workflowName: gcs-finalizer-workflow # Replace with your workflow path + location: us-central1 # desired location + project: my-project-name # Replace with your project name +``` + +They can then check the status of the applied resource: + +``` +kubectl get gcsbucketwithfinalizertrigger -n config-connector +kubectl get gcsbucketwithfinalizertrigger gcsevent-test -n config-connector -o yaml +``` + +Navigate to GCS page in the GCP Console and verify the bucket creation. Also verify that the Triggers are setup correctly in the EventArc page. + +Once done, the user can delete the `GCSBucketWithFinalizerTrigger` instance: + +``` +kubectl delete gcsbucketwithfinalizertrigger gcsevent-test -n config-connector +``` + +## Administrator: ResourceGraphDefinition +The administrator needs to install the RGD in the cluster first before the user can consume it: + +``` +kubectl apply -f rgd.yaml +``` + +Validate the RGD is installed correctly: + +``` +kubectl get rgd gcsbucketwithfinalizertrigger.kro.run +``` + +Once all user created instances are deleted, the administrator can choose to deleted the RGD. + +
+ ResourceGraphDefinition + ```yaml title="rgd.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: gcsbucketwithfinalizertrigger.kro.run +spec: + schema: + apiVersion: v1alpha1 + kind: GCSBucketWithFinalizerTrigger + spec: + name: string + workflowName: string + location: string + project: string + status: + url: ${bucket.status.url} + resources: + - id: storageEnable + template: + apiVersion: serviceusage.cnrm.cloud.google.com/v1beta1 + kind: Service + metadata: + annotations: + cnrm.cloud.google.com/deletion-policy: "abandon" + cnrm.cloud.google.com/disable-dependent-services: "false" + name: storage-enablement + spec: + resourceID: storage.googleapis.com + - id: iamEnable + template: + apiVersion: serviceusage.cnrm.cloud.google.com/v1beta1 + kind: Service + metadata: + annotations: + cnrm.cloud.google.com/deletion-policy: "abandon" + cnrm.cloud.google.com/disable-dependent-services: "false" + name: iam-enablement + spec: + resourceID: iam.googleapis.com + - id: pubsubEnable + template: + apiVersion: serviceusage.cnrm.cloud.google.com/v1beta1 + kind: Service + metadata: + annotations: + cnrm.cloud.google.com/deletion-policy: "abandon" + cnrm.cloud.google.com/disable-dependent-services: "false" + name: pubsub-enablement + spec: + resourceID: pubsub.googleapis.com + - id: eventarcEnable + template: + apiVersion: serviceusage.cnrm.cloud.google.com/v1beta1 + kind: Service + metadata: + annotations: + cnrm.cloud.google.com/deletion-policy: "abandon" + cnrm.cloud.google.com/disable-dependent-services: "false" + name: eventarc-enablement + spec: + resourceID: eventarc.googleapis.com + - id: iamsa + template: + apiVersion: iam.cnrm.cloud.google.com/v1beta1 + kind: IAMServiceAccount + metadata: + labels: + enabled-service: ${iamEnable.metadata.name} + #annotations: + # cnrm.cloud.google.com/project-id: ${schema.spec.project} + name: ${schema.spec.name} + spec: + displayName: ${schema.spec.name}-eventarc-workflow + - id: iampmEventarc + template: + apiVersion: iam.cnrm.cloud.google.com/v1beta1 + kind: IAMPolicyMember + metadata: + labels: + enabled-service: ${iamEnable.metadata.name} + name: ${schema.spec.name}-eventarc + spec: + memberFrom: + serviceAccountRef: + name: ${iamsa.metadata.name} + role: roles/eventarc.admin + resourceRef: + kind: Project + external: ${schema.spec.project} + - id: iampmWorkflow + template: + apiVersion: iam.cnrm.cloud.google.com/v1beta1 + kind: IAMPolicyMember + metadata: + labels: + enabled-service: ${iamEnable.metadata.name} + name: ${schema.spec.name}-workflow + spec: + memberFrom: + serviceAccountRef: + name: ${iamsa.metadata.name} + role: roles/workflows.admin + resourceRef: + kind: Project + external: ${schema.spec.project} + - id: topic + template: + apiVersion: pubsub.cnrm.cloud.google.com/v1beta1 + kind: PubSubTopic + metadata: + labels: + enabled-service: ${pubsubEnable.metadata.name} + name: ${schema.spec.name}-gcs-finalizer-topic + - id: bucket + template: + apiVersion: storage.cnrm.cloud.google.com/v1beta1 + kind: StorageBucket + metadata: + labels: + enabled-service: ${storageEnable.metadata.name} + name: ${schema.spec.name}-${schema.spec.project} + spec: + uniformBucketLevelAccess: true + - id: eventTrigger + template: + apiVersion: eventarc.cnrm.cloud.google.com/v1beta1 + kind: EventarcTrigger + metadata: + labels: + enabled-service: ${eventarcEnable.metadata.name} + name: ${schema.spec.name}-gcsfinalizer + spec: + destination: + workflowRef: + external: "projects/${schema.spec.project}/locations/${schema.spec.location}/workflows/${schema.spec.workflowName}" + location: ${schema.spec.location} + serviceAccountRef: + name: ${iamsa.metadata.name} + transport: + pubsub: + topicRef: + name: ${topic.metadata.name} + namespace: config-connector + matchingCriteria: + - attribute: "type" + value: "google.cloud.pubsub.topic.v1.messagePublished" + projectRef: + external: "projects/${schema.spec.project}" + - id: storageNotification + template: + apiVersion: storage.cnrm.cloud.google.com/v1beta1 + kind: StorageNotification + metadata: + name: ${schema.spec.name}-gcs + spec: + bucketRef: + name: ${bucket.metadata.name} + topicRef: + name: ${topic.metadata.name} + eventTypes: + - "OBJECT_FINALIZE" + payloadFormat: JSON_API_V1 + ``` +
diff --git a/website/versioned_docs/version-0.6.3/examples/gcp/gcsbucket-with-finalizer-trigger.png b/website/versioned_docs/version-0.6.3/examples/gcp/gcsbucket-with-finalizer-trigger.png new file mode 100644 index 000000000..8bfdd20fd Binary files /dev/null and b/website/versioned_docs/version-0.6.3/examples/gcp/gcsbucket-with-finalizer-trigger.png differ diff --git a/website/versioned_docs/version-0.6.3/examples/gcp/gke-cluster.md b/website/versioned_docs/version-0.6.3/examples/gcp/gke-cluster.md new file mode 100644 index 000000000..6b4cbd3e0 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/gcp/gke-cluster.md @@ -0,0 +1,233 @@ +--- +sidebar_position: 405 +--- + +# GKECluster + +A **Platform Administrator** wants to give end users in their organization self-service access to create GKE clusters. The platform administrator creates a kro ResourceGraphDefinition called *gkecluster.kro.run* that defines the required Kubernetes resources and a CRD called *GKEcluster* that exposes only the options they want to be configurable by end users. The ResourceGraphDefinition would define the following resources (using [KCC](https://github.com/GoogleCloudPlatform/k8s-config-connector) to provide the mappings from K8s CRDs to Google Cloud APIs): + +* GKE cluster +* Container Node Pool +* Network +* Subnetwork +* KMSKeyRing - Encrypt BootDisk +* KMSCryptoKey - Encrypt BootDisk + +Everything related to these resources would be hidden from the end user, simplifying their experience. + +![GKE Cluster Stack](gke-cluster.png) + +## End User: GKECluster + +The administrator needs to install the RGD first. +The end user creates a `GKECluster` resource something like this: + +```yaml +apiVersion: kro.run/v1alpha1 +kind: GKECluster +metadata: + name: krodemo + namespace: config-connector +spec: + name: krodemo # Name used for all resources created as part of this RGD + location: us-central1 # Region where the GCP resources are created + maxnodes: 4 # Max scaling limit for the nodes in the new nodepool +``` + +They can then check the status of the applied resource: + +``` +kubectl get gkeclusters +kubectl get gkeclusters krodemo -n config-connector -o yaml +``` + +Navigate to GKE Cluster page in the GCP Console and verify the cluster creation. + +Once done, the user can delete the `GKECluster` instance: + +``` +kubectl delete gkecluster krodemo -n config-connector +``` + +## Administrator: ResourceGraphDefinition +The administrator needs to install the RGD in the cluster first before the user can consume it: + +``` +kubectl apply -f rgd.yaml +``` + +Validate the RGD is installed correctly: + +``` +kubectl get rgd gkecluster.kro.run +``` + +Once all user created instances are deleted, the administrator can choose to deleted the RGD. + +
+ ResourceGraphDefinition + ```yaml title="rgd.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: gkecluster.kro.run +spec: + schema: + apiVersion: v1alpha1 + kind: GKECluster + spec: + name: string + nodepool: string + maxnodes: integer + location: string + status: + masterVersion: ${cluster.status.masterVersion} + resources: + - id: network + template: + apiVersion: compute.cnrm.cloud.google.com/v1beta1 + kind: ComputeNetwork + metadata: + labels: + source: "gkecluster" + name: ${schema.spec.name} + spec: + #routingMode: GLOBAL + #deleteDefaultRoutesOnCreate: false + routingMode: REGIONAL + autoCreateSubnetworks: false + - id: subnet + template: + apiVersion: compute.cnrm.cloud.google.com/v1beta1 + kind: ComputeSubnetwork + metadata: + labels: + source: "gkecluster" + name: ${network.metadata.name} + spec: + ipCidrRange: 10.2.0.0/16 + #ipCidrRange: 10.10.90.0/24 + region: ${schema.spec.location} + networkRef: + name: ${schema.spec.name} + #privateIpGoogleAccess: true + - id: topic + template: + apiVersion: pubsub.cnrm.cloud.google.com/v1beta1 + kind: PubSubTopic + metadata: + labels: + source: "gkecluster" + name: ${subnet.metadata.name} + - id: keyring + template: + apiVersion: kms.cnrm.cloud.google.com/v1beta1 + kind: KMSKeyRing + metadata: + labels: + source: "gkecluster" + name: ${topic.metadata.name} + spec: + location: ${schema.spec.location} + - id: key + template: + apiVersion: kms.cnrm.cloud.google.com/v1beta1 + kind: KMSCryptoKey + metadata: + labels: + source: "gkecluster" + name: ${keyring.metadata.name} + spec: + keyRingRef: + name: ${schema.spec.name} + purpose: ASYMMETRIC_SIGN + versionTemplate: + algorithm: EC_SIGN_P384_SHA384 + protectionLevel: SOFTWARE + importOnly: false + - id: nodepool + template: + apiVersion: container.cnrm.cloud.google.com/v1beta1 + kind: ContainerNodePool + metadata: + labels: + source: "gkecluster" + name: ${cluster.metadata.name} + spec: + location: ${schema.spec.location} + autoscaling: + minNodeCount: 1 + maxNodeCount: ${schema.spec.maxnodes} + nodeConfig: + machineType: n1-standard-1 + diskSizeGb: 100 + diskType: pd-standard + #taint: + #- effect: NO_SCHEDULE + # key: originalKey + # value: originalValue + clusterRef: + name: ${schema.spec.name} + - id: cluster + template: + apiVersion: container.cnrm.cloud.google.com/v1beta1 + kind: ContainerCluster + metadata: + #annotations: + # cnrm.cloud.google.com/remove-default-node-pool: "false" + labels: + source: "gkecluster" + name: ${key.metadata.name} + spec: + location: ${schema.spec.location} + initialNodeCount: 1 + networkRef: + name: ${schema.spec.name} + subnetworkRef: + name: ${schema.spec.name} + ipAllocationPolicy: + clusterIpv4CidrBlock: /20 + servicesIpv4CidrBlock: /20 + #masterAuth: + # clientCertificateConfig: + # issueClientCertificate: false + #workloadIdentityConfig: + # # Workload Identity supports only a single namespace based on your project name. + # # Replace ${PROJECT_ID?} below with your project ID. + # workloadPool: ${PROJECT_ID?}.svc.id.goog + notificationConfig: + pubsub: + enabled: true + topicRef: + name: ${schema.spec.name} + loggingConfig: + enableComponents: + - "SYSTEM_COMPONENTS" + - "WORKLOADS" + monitoringConfig: + enableComponents: + - "SYSTEM_COMPONENTS" + - "APISERVER" + managedPrometheus: + enabled: true + clusterAutoscaling: + enabled: true + autoscalingProfile: BALANCED + resourceLimits: + - resourceType: cpu + maximum: 100 + minimum: 10 + - resourceType: memory + maximum: 1000 + minimum: 100 + autoProvisioningDefaults: + bootDiskKMSKeyRef: + name: ${schema.spec.name} + nodeConfig: + linuxNodeConfig: + sysctls: + net.core.somaxconn: "4096" + cgroupMode: "CGROUP_MODE_UNSPECIFIED" + + ``` +
diff --git a/website/versioned_docs/version-0.6.3/examples/gcp/gke-cluster.png b/website/versioned_docs/version-0.6.3/examples/gcp/gke-cluster.png new file mode 100644 index 000000000..37eaf0872 Binary files /dev/null and b/website/versioned_docs/version-0.6.3/examples/gcp/gke-cluster.png differ diff --git a/website/versioned_docs/version-0.6.3/examples/kubernetes/category.json b/website/versioned_docs/version-0.6.3/examples/kubernetes/category.json new file mode 100644 index 000000000..d17bd0a06 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/kubernetes/category.json @@ -0,0 +1,10 @@ +{ + "label": "Kubernetes Examples", + "position": 500, + "link": { + "type": "generated-index", + "description": "Examples of using Kro with Kubernetes" + }, + "collapsible": true, + "collapsed": true +} diff --git a/website/versioned_docs/version-0.6.3/examples/kubernetes/deploying-coredns.md b/website/versioned_docs/version-0.6.3/examples/kubernetes/deploying-coredns.md new file mode 100644 index 000000000..d10d65472 --- /dev/null +++ b/website/versioned_docs/version-0.6.3/examples/kubernetes/deploying-coredns.md @@ -0,0 +1,189 @@ +--- +sidebar_position: 10 +--- + +# CoreDNS Deployment + +```yaml title="coredns-rg.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGraphDefinition +metadata: + name: coredns.kro.run +spec: + schema: + apiVersion: v1alpha1 + kind: CoreDNSDeployment + spec: + name: string | default=mycoredns + namespace: string | default=default + values: + clusterRole: + labels: 'map[string]string | default={"eks.amazonaws.com/component": "coredns", "k8s-app": "kube-dns", "kubernetes.io/bootstrapping": "rbac-defaults"}' + clusterRoleBinding: + annotations: 'map[string]string | default={"rbac.authorization.kubernetes.io/autoupdate": "\"true\""}' + configMap: + labels: 'map[string]string | default={"eks.amazonaws.com/component": "coredns", "k8s-app": "kube-dns"}' + deployment: + annotations: 'map[string]string | default={"deployment.kubernetes.io/revision": "\"1\""}' + labels: 'map[string]string | default={"eks.amazonaws.com/component": "coredns", "k8s-app": "kube-dns", "kubernetes.io/name": "CoreDNS"}' + replicas: integer | default=2 + image: + repository: string | default=coredns/coredns + tag: string | default=1.11.3 + resources: + limits: + cpu: string | default=100m + memory: string | default=128Mi + requests: + cpu: string | default=100m + memory: string | default=128Mi + service: + annotations: 'map[string]string | default={"prometheus.io/port": "9153", "prometheus.io/scrape": "true"}' + labels: 'map[string]string | default={"eks.amazonaws.com/component": "kube-dns", "k8s-app": "kube-dns", "kubernetes.io/cluster-service": "true", "kubernetes.io/name": "CoreDNS"}' + clusterIP: string | default=10.100.123.45 + clusterIPs: '[]string | default=["10.100.123.45"]' + ipFamilies: '[]string | default=["IPv4"]' + type: string | default=ClusterIP + serviceAccount: + secrets: 'map[string]string | default={"name": "coredns-token-pvcnf"}' + resources: + - id: clusterRole + template: + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: ${schema.spec.name} + labels: ${schema.spec.values.clusterRole.labels} + rules: + - apiGroups: + - "" + resources: + - endpoints + - services + - pods + - namespaces + verbs: + - list + - watch + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - id: clusterRoleBinding + template: + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: ${schema.spec.name} + labels: ${schema.spec.values.clusterRole.labels} + annotations: ${schema.spec.values.clusterRoleBinding.annotations} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ${clusterRole.metadata.name} + subjects: + - kind: ServiceAccount + name: ${serviceAccount.metadata.name} + namespace: ${serviceAccount.metadata.namespace} + - id: configMap + template: + apiVersion: v1 + kind: ConfigMap + metadata: + name: ${schema.spec.name} + labels: ${schema.spec.values.configMap.labels} + data: + Corefile: |- + .:53 { + errors + health + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + } + prometheus :9153 + forward . /etc/resolv.conf + cache 30 + loop + reload + loadbalance + } + - id: deployment + template: + apiVersion: apps/v1 + kind: Deployment + metadata: + annotations: ${schema.spec.values.deployment.annotations} + labels: ${schema.spec.values.deployment.labels} + name: ${schema.spec.name} + spec: + replicas: ${schema.spec.values.deployment.replicas} + selector: + matchLabels: ${schema.spec.values.configMap.labels} + template: + metadata: + labels: ${schema.spec.values.configMap.labels} + spec: + serviceAccountName: ${serviceAccount.metadata.name} + containers: + - name: "coredns" + image: ${schema.spec.values.image.repository}:${schema.spec.values.image.tag} + args: ["-conf", "/etc/coredns/Corefile"] + resources: ${schema.spec.values.resources} + volumeMounts: + - name: config-volume + mountPath: /etc/coredns + volumes: + - name: config-volume + configMap: + name: ${schema.spec.name} + items: + - key: Corefile + path: Corefile + - id: service + template: + apiVersion: v1 + kind: Service + metadata: + name: ${schema.spec.name} + labels: ${schema.spec.values.service.labels} + annotations: ${schema.spec.values.service.annotations} + spec: + selector: + k8s-app: kube-dns + clusterIP: ${schema.spec.values.service.clusterIP} + clusterIPs: ${schema.spec.values.service.clusterIPs} + internalTrafficPolicy: Cluster + ipFamilies: ${schema.spec.values.service.ipFamilies} + ports: + - name: dns + port: 53 + protocol: UDP + targetPort: 53 + - name: dns-tcp + port: 53 + protocol: TCP + targetPort: 53 + selector: + k8s-app: kube-dns + sessionAffinity: None + - id: serviceAccount + template: + apiVersion: v1 + kind: ServiceAccount + metadata: + name: ${schema.spec.name} + namespace: ${schema.spec.namespace} + labels: ${schema.spec.values.configMap.labels} + secrets: + - ${schema.spec.values.serviceAccount.secrets} +``` diff --git a/website/versioned_sidebars/version-0.6.3-sidebars.json b/website/versioned_sidebars/version-0.6.3-sidebars.json new file mode 100644 index 000000000..21abb2fb5 --- /dev/null +++ b/website/versioned_sidebars/version-0.6.3-sidebars.json @@ -0,0 +1,20 @@ +{ + "docsSidebar": [ + { + "type": "autogenerated", + "dirName": "docs" + } + ], + "examplesSidebar": [ + { + "type": "autogenerated", + "dirName": "examples" + } + ], + "apisSidebar": [ + { + "type": "autogenerated", + "dirName": "api" + } + ] +} diff --git a/website/versions.json b/website/versions.json index 737850f88..297df473c 100644 --- a/website/versions.json +++ b/website/versions.json @@ -1,4 +1,5 @@ [ + "0.6.3", "0.6.2", "0.6.1", "0.6.0",