diff --git a/examples/cachecluster/cachecluster-instance.yaml b/examples/cachecluster/cachecluster-instance.yaml index 6197f88..a0cc839 100644 --- a/examples/cachecluster/cachecluster-instance.yaml +++ b/examples/cachecluster/cachecluster-instance.yaml @@ -1,6 +1,6 @@ apiVersion: kro.run/v1alpha1 -kind: Redis +kind: Valkey metadata: name: my-cache-subnetgroup spec: - name: my-cache-subnetgroup \ No newline at end of file + name: my-cache-subnetgroup diff --git a/examples/cachecluster/cachecluster.yaml b/examples/cachecluster/cachecluster.yaml index ac8c602..45fd74f 100644 --- a/examples/cachecluster/cachecluster.yaml +++ b/examples/cachecluster/cachecluster.yaml @@ -1,68 +1,68 @@ apiVersion: kro.run/v1alpha1 kind: ResourceGroup metadata: - name: redis.kro.run + name: valkey.kro.run spec: schema: apiVersion: v1alpha1 - kind: Redis + kind: Valkey spec: name: string status: csgARN: ${cacheSubnetGroup.status.ackResourceMetadata.arn} subnets: ${cacheSubnetGroup.status.subnets} - clusterARN: ${redis.status.ackResourceMetadata.arn} + clusterARN: ${valkey.status.ackResourceMetadata.arn} resources: - - name: networkingStack - template: - apiVersion: kro.run/v1alpha1 - kind: NetworkingStack - metadata: - name: ${schema.spec.name}-networking-stack - spec: - name: ${schema.spec.name}-networking-stack - - name: cacheSubnetGroup - template: - apiVersion: elasticache.services.k8s.aws/v1alpha1 - kind: CacheSubnetGroup - metadata: - name: ${schema.spec.name}-redis-subnet-group - spec: - cacheSubnetGroupDescription: "Redis ElastiCache subnet group" - cacheSubnetGroupName: ${schema.spec.name}-redis-subnet-group - subnetIDs: - - ${networkingStack.status.networkingInfo.subnetAZA} - - ${networkingStack.status.networkingInfo.subnetAZB} - - ${networkingStack.status.networkingInfo.subnetAZC} - - name: sg - template: - apiVersion: ec2.services.k8s.aws/v1alpha1 - kind: SecurityGroup - metadata: - name: ${schema.spec.name}-redis-sg - spec: - name: ${schema.spec.name}-redis-sg - description: "Redis ElastiCache security group" - vpcID: ${networkingStack.status.networkingInfo.vpcID} - ingressRules: - - fromPort: 6379 - toPort: 6379 - ipProtocol: tcp - ipRanges: - - cidrIP: 0.0.0.0/0 - - name: redis - template: - apiVersion: elasticache.services.k8s.aws/v1alpha1 - kind: CacheCluster - metadata: - name: ${schema.spec.name}-redis - spec: - cacheClusterID: vote-redis-cluster - cacheNodeType: cache.t3.micro - cacheSubnetGroupName: ${schema.spec.name}-redis-subnet-group - engine: redis - engineVersion: "7.1" - numCacheNodes: 1 - port: 6379 - securityGroupIDs: - - ${sg.status.id} \ No newline at end of file + - name: networkingStack + template: + apiVersion: kro.run/v1alpha1 + kind: NetworkingStack + metadata: + name: ${schema.spec.name}-networking-stack + spec: + name: ${schema.spec.name}-networking-stack + - name: 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} + - name: 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 + - name: 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/examples/cachecluster/simple-cachecluster.yaml b/examples/cachecluster/simple-cachecluster.yaml index a7997e5..1a55a87 100644 --- a/examples/cachecluster/simple-cachecluster.yaml +++ b/examples/cachecluster/simple-cachecluster.yaml @@ -1,11 +1,11 @@ apiVersion: kro.run/v1alpha1 kind: ResourceGroup metadata: - name: redis.kro.run + name: valkey.kro.run spec: schema: apiVersion: v1alpha1 - kind: Redis + kind: Valkey spec: name: string subnetIDs: "[]string" @@ -13,13 +13,13 @@ spec: csgARN: ${cacheSubnetGroup.status.ackResourceMetadata.arn} subnets: ${cacheSubnetGroup.status.subnets} resources: - - name: cacheSubnetGroup - template: - apiVersion: elasticache.services.k8s.aws/v1alpha1 - kind: CacheSubnetGroup - metadata: - name: ${schema.spec.name}-redis-subnet-group - spec: - cacheSubnetGroupDescription: "Redis ElastiCache subnet group" - cacheSubnetGroupName: ${schema.spec.name}-redis-subnet-group - subnetIDs: ${schema.spec.subnetIDs} \ No newline at end of file + - name: 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: ${schema.spec.subnetIDs} diff --git a/website/docs/docs/concepts/00-resource-groups.md b/website/docs/docs/concepts/00-resource-groups.md index f31e389..0a61f9d 100644 --- a/website/docs/docs/concepts/00-resource-groups.md +++ b/website/docs/docs/concepts/00-resource-groups.md @@ -4,131 +4,156 @@ sidebar_position: 1 # ResourceGroups -**ResourceGroups** are the fundamental building blocks in **kro**. They provide -a way to define, organize, and manage sets of related Kubernetes resources as a +ResourceGroups 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 **ResourceGroup**? +## What is a ResourceGroup? -A **ResourceGroup** is a custom resource that serves as a blueprint for creating -and managing a collection of Kubernetes resources. It allows you to: +A **ResourceGroup** is a custom resource that lets you create new Kubernetes +APIs for deploying multiple resources together. It acts as a blueprint, +defining: -- Define multiple resources in a single, cohesive unit -- Establish relationships and dependencies between resources -- Create high-level abstractions of complex Kubernetes configurations -- Promote reusability and consistency across your infrastructure +- 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) -## Anatomy of a **ResourceGroup** +When you create a **ResourceGroup**, kro generates a new API (a.k.a Custom +Resource Defintion) in your cluster that others can use to deploy resources in a +consistent, controlled way. -A **ResourceGroup**, like any Kubernetes resource, consists of three main parts: +## Anatomy of a ResourceGroup -1. **Metadata**: Name, namespace, labels, etc. +A ResourceGroup, like any Kubernetes resource, consists of three main parts: + +1. **Metadata**: name, namespace, labels, etc. 2. **Spec**: Defines the structure and properties of the ResourceGroup 3. **Status**: Reflects the current state of the ResourceGroup -The `spec` section of a ResourceGroup typically includes: +The `spec` section of a ResourceGroup 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 -- **Parameters**: Define the customizable aspects of the ResourceGroup -- **Resources**: Specify the Kubernetes resources to be created -- The **kind** and **apiVersion** fields within the spec define the CRD that - will be generated for this ResourceGroup. Here's a simple example of a - ResourceGroup: +This structure translates to YAML as follows: -```yaml text title="simple-web-app.yaml" -apiVersion: kro.run/v1 +```yaml +apiVersion: kro.run/v1alpha1 kind: ResourceGroup metadata: - name: simple-web-app + name: my-resourcegroup # Metadata section spec: - kind: SimpleWebApp - apiVersion: v1alpha1 - parameters: - appName: string - image: string - replicas: int + 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: - - name: deployment - definition: - apiVersion: apps/v1 - kind: Deployment - metadata: - name: ${schema.spec.appName}-deployment - spec: - replicas: ${schema.spec.replicas} - selector: - matchLabels: - app: ${schema.spec.appName} - template: - metadata: - labels: - app: ${schema.spec.appName} - spec: - containers: - - name: ${schema.spec.appName}-container - image: ${schema.spec.image} - - name: service - definition: - apiVersion: v1 - kind: Service - metadata: - name: ${schema.spec.appName}-service - spec: - selector: - app: ${schema.spec.appName} - ports: - - port: 80 - targetPort: 80 + - name: resource1 + # declare your resources along with default values and variables + template: {} ``` -In this example, the **ResourceGroup** defines a simple web application with a -Deployment and a Service. The appName, image, and replicas are parameters that -can be set when instantiating this ResourceGroup. +Let's look at each component in detail... + +## Understanding the Schema -## **ResourceGroup** Processing +The schema section defines your new API's structure. It determines: -When a **ResourceGroup** is submitted to the Kubernetes API server, the kro -controller processes it as follows: +- What fields users can configure when creating instances +- What status information they can view +- Type validation and default values -1. **Formal Verification**: The controller performs a comprehensive analysis of - the ResourceGroup definition. This includes: +Here's an example schema: - - **Syntax checking**: Ensuring all fields are correctly formatted. - - **Type checking**: Validating that parameter types match their definitions. - - **Semantic validation**: Verifying that resource relationships and - dependencies are logically sound. - - **Dry-run validation**: Simulating the creation of resources to detect - potential issues. +```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} +``` -2. **CRD Generation**: The controller automatically generates a new **Custom - Resource Definition (CRD)** based on the ResourceGroup's specification. This - CRD represents the type for instances of this ResourceGroup. +**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 ResourceGroup. +kro automatically: -3. **CRD Registration**: It registers the newly generated CRD with the - Kubernetes API server, making it available for use in the cluster. +- Infers the correct types from your expressions +- Validates that referenced resources exist +- Updates these fields as your resources change -4. **Micro-Controller Deployment**: kro deploys a dedicated micro-controller for - this ResourceGroup. This micro-controller will listen for **"instance" - events** - instances of the CRD created in step 2. It will be responsible for - managing the **lifecycle of resources** defined in the ResourceGroup for each - instance. +## ResourceGroup Processing -5. **Status Update**: The controller updates the status of the ResourceGroup to - reflect that the corresponding CRD has been created and registered. +When you create a **ResourceGroup**, kro processes it in several steps to ensure +correctness and set up the necessary components: -For example, given our `simple-web-app` ResourceGroup, the controller would -create and register a CRD named `SimpleWebApps` (plural form of the -ResourceGroup name). This CRD defines the structure for creating instances of -the web application with customizable parameters. The deployed micro-controller -would then manage all **SimpleWebApps instances**, creating and managing the -associated **Deployments** and **Services** as defined in the ResourceGroup. +1. **Validation**: kro validates your **ResourceGroup** 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: -The **kro** controller continues to monitor the **ResourceGroup** for any -changes, updating the corresponding CRD and micro-controller as necessary. + - 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 Acycled Graph(DAG) + without cycles + - Validates all CEL expressions in status fields and conditions -## **ResourceGroup** Instance Example +2. **API Generation**: kro generates and registers a new CRD in your cluster + based on your schema. For example, if your **ResourceGroup** defines a + `WebApplication` API, kro creates a CRD that: -After the **ResourceGroup** is processed, users can create instances of it. -Here's an example of how an instance for the `SimpleWebApp` might look: + - 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: + + - 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 + +For instance, when you create a `WebApplication` ResourceGroup, 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 ResourceGroup for changes, updating the API and +its behavior accordingly. + +## ResourceGroup Instance Example + +After the **ResourceGroup** is validated and registered in the cluster, users +can can create instances of it. Here's an example of how an instance for the +`SimpleWebApp` might look: ```yaml title="my-web-app-instance.yaml" apiVersion: kro.run/v1alpha1 @@ -140,6 +165,3 @@ spec: image: nginx:latest replicas: 3 ``` - -In the next section, we'll explore the `parameters` and `resources` sections of -a **ResourceGroup** in more detail. diff --git a/website/docs/docs/concepts/10-simple-schema.md b/website/docs/docs/concepts/10-simple-schema.md index cc8d701..10ed828 100644 --- a/website/docs/docs/concepts/10-simple-schema.md +++ b/website/docs/docs/concepts/10-simple-schema.md @@ -4,8 +4,9 @@ sidebar_position: 2 # Simple Schema -kro's Simple Schema provides a powerful yet intuitive way to define the -structure of your ResourceGroup. Here is comprehensive example: +**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 @@ -13,174 +14,181 @@ kind: ResourceGroup metadata: name: web-application spec: - apiVersion: v1alpha1 - kind: WebApplication - parameters: + schema: + apiVersion: v1alpha1 + kind: WebApplication spec: - name: string | required=true description="Name of the web application" - replicas: integer | default=1 minimum=1 maximum=10 + # Basic types + name: string | required=true description="My Name" + replicas: integer | default=1 minimum=1 maximum=100 image: string | required=true - ports: - - port: integer | required=true - targetPort: integer - env: 'map[string]string' - config: ConfigType - configArray: []ConfigType - customTypes: - ConfigType: - logLevel: string | enum="debug,info,warn,error" default="info" - maxConnections: integer | minimum=1 maximum=1000 + + # Structured type + ingress: + enabled: boolean | default=false + host: string | default="example.com" + path: string | default="/" + + # Array type + ports: "[]integer" + + # Map type + env: "map[string]string" + status: - url: ${service.status.loadBalancer.ingress[0].hostname}" - resources: [] + # Status fields with auto-inferred types + availableReplicas: ${deployment.status.availableReplicas} + serviceEndpoint: ${service.status.loadBalancer.ingress[0].hostname} ``` -## Simple Schema Features Explained +## Type Definitions -### 1. Spec Field Definition +### Basic Types -#### Basic Types +kro supports these foundational types: -- `string`: Basic string type -- `integer`: Whole number -- `boolean`: True/False value +- `string`: Text values +- `integer`: Whole numbers +- `boolean`: True/False values +- `number`: Decimal numbers -for example to define a field that is a string, you can define it as follows: +For example: ```yaml name: string age: integer +enabled: boolean +price: number ``` -#### Structure types - -Structure types or object types are defined by specifying the fields within the -object. The fields can be of basic types or other structure types. +### Structure Types -for example to define a structure type for a person with name and age fields, -you can define it as follows: +You can create complex objects by nesting fields. Each field can use any type, +including other structures: ```yaml -person: +# Simple structure +address: + street: string + city: string + zipcode: string + +# Nested structures +user: name: string - age: integer + address: # Nested object + street: string + city: string + contacts: "[]string" # Array of strings ``` -#### Map Types +### Array Types -- Arrays: Denoted by `[]`, e.g., `'[]string'` -- Maps: Denoted by `map[keyType]valueType`, e.g., `'map[string]string'` and - `'map[string]Person'` +Arrays are denoted using `[]` syntax: -### 2. Validation and Documentation Markers +- Basic arrays: `[]string`, `[]integer`, `[]boolean` -In addition to the type, fields can also have markers for validation, -documentation and other purposes that are OpenAPISchema compatible. - -For example to define a field that is required, has a default value and a -description, you can define it as follows: +Examples: ```yaml -person: - name: - string | required=true default="Kylian Mbappé" description="Name of the - person" +tags: []string +ports: []integer ``` -Currently supported markers include: +### Map Types -- `required=true`: Field must be provided -- `default=value`: Default value if not provided -- `description="..."`: Provides documentation for the field -- `enum="value1,value2,..."`: Restricts to a set of values **NOT IMPLEMENTED** -- `minimum=value` and `maximum=value`: For numeric constraints **NOT - IMPLEMENTED** +Maps are key-value pairs denoted as `map[keyType]valueType`: + +- `map[string]string`: String to string mapping +- `map[string]integer`: String to integer mapping -### 3. Custom Types Definition +Examples: + +```yaml +labels: "map[string]string" +metrics: "map[string]number" +``` -Custom types are defined in the `customTypes` section, allowing for reusable -complex structures. They can be referenced by name in the spec or status fields. +## Validation and Documentation -Example: +Fields can have multiple markers for validation and documentation: ```yaml -customTypes: - ConfigType: - logLevel: string | enum="debug,info,warn,error" default="info" - maxConnections: integer | minimum=1 maximum=1000 -spec: - config: ConfigType | required=true +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" ``` -### 4. Status Field Definition +### 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 + +Multiple markers can be combined using the `|` separator. -Status fields are defined similarly to spec fields and can include validation -and documentation markers. However on top of that, status fields can also -include value markers: +For example: -#### Value Marker **NOT IMPLEMENTED** +```yaml +name: string | required=true default="app" description="Application name" +``` -- `value="${resource.status.field}"`: Specifies that this field's value should - be dynamically obtained from another resource. The value is a CEL expression - that is validated at ResourceGroup processing time and evaluated at runtime. +## Status Fields -:::tip Note that the value marker is a kro extension to the OpenAPISchema and is -not part of the official OpenAPISchema specification. ::: +Status fields use CEL expressions to reference values from resources. kro +automatically: -Example: +- Infers the correct types from the expressions +- Validates that referenced resources exist +- Updates values when the underlying resources change ```yaml status: - url: string | value="${service.status.loadBalancer.ingress[0].hostname}" + # Types are inferred from the referenced fields + availableReplicas: ${deployment.status.availableReplicas} + endpoint: ${service.status.loadBalancer.ingress[0].hostname} ``` -## Default status fields - -**kro** automatically injects two common fields into the status of all instances -generated from **ResourceGroups**: `conditions` and `state`. These fields -provide essential information about the current status of the instance and its -associated resources. +## Default Status Fields -:::tip `conditions` and `state` are reserved words in the status section. If a -user defines these fields in their **ResourceGroup**'s status schema, kro will -override them with its own values. ::: +kro automatically injects two fields to every instance's status: ### 1. Conditions -The `conditions` field is an array of condition objects, each representing a -specific aspect of the instance's state. kro automatically manages this field. +An array of condition objects tracking the instance's state: ```yaml status: - conditions: "[]condition" -customTypes: - condition: - type: string - status: string | enum="True,False,Unknown" - lastTransitionTime: string - reason: string - message: string + conditions: + - type: string # e.g., "Ready", "Progressing" + status: string # "True", "False", "Unknown" + lastTransitionTime: string + reason: string + message: string ``` -Common condition types include: +Common condition types: -- `Ready`: Indicates whether the instance is fully reconciled and operational. -- `Progressing`: Shows if the instance is in the process of reaching the desired - state. -- `Degraded`: Signals that the instance is operational but not functioning - optimally. -- `Error`: Indicates that an error has occurred during reconciliation. +- `Ready`: Instance is fully reconciled +- `Progressing`: Working towards desired state +- `Degraded`: Operational but not optimal +- `Error`: Reconciliation error occurred ### 2. State -The `state` field provides a high-level summary of the instance's current -status. +A high-level summary of the instance's status: ```yaml status: - state: string | enum="Ready,Progressing,Degraded,Error,Terminating,Unknown" + state: string # Ready, Progressing, Degraded, Unknown, Deleting ``` -> These default status fields are automatically added to every instance's -> status, providing a consistent way to check the health and state of resources -> across different **ResourceGroups**. +:::tip + +`conditions` and `state` are reserved words. If defined in your schema, kro will +override them with its own values. + +::: diff --git a/website/docs/docs/concepts/15-instances.md b/website/docs/docs/concepts/15-instances.md index 993b027..2f119b5 100644 --- a/website/docs/docs/concepts/15-instances.md +++ b/website/docs/docs/concepts/15-instances.md @@ -4,120 +4,71 @@ sidebar_position: 15 # Instances -Instances are a fundamental concept in **kro** that represent instances of -ResourceGroups. They define the desired state of a set of resources, which kro -continuously works to maintain. +Once **kro** processes your ResourceGroup, it creates a new API in your cluster. +Users can then create instances of this API to deploy resources in a consistent, +controlled way. -## What is an Instance? +## Understanding Instances -An Instance is a Kubernetes custom resource that: - -- References a specific ResourceGroup -- Provides values for the parameters defined in the ResourceGroup -- Represents the desired state of a set of Kubernetes resources - -## Anatomy of an Instance - -Here's an example of an Instance for a `WebApplication` ResourceGroup: +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 +apiVersion: v1alpha1 kind: WebApplication metadata: - name: my-web-app + name: my-app spec: - name: awesome-app - replicas: 3 + name: web-app image: nginx:latest - ports: - - port: 80 - targetPort: 8080 - env: - DB_HOST: mydb.example.com - LOG_LEVEL: debug + ingress: + enabled: true ``` -::: - -tip The spec fields of an Instance correspond to the parameters defined in the -ResourceGroup. - -::: - -## The reconciliation loop - -kro manages Instances through a continuous reconciliation process: - -- **Desired state detection**: kro observes the Instance, which represents the - desired state of resources. -- **Current state assessment**: kro talks to the api-server and checks the - current state of resources in the cluster related to the Instance. -- **Difference identification**: Any differences between the desired state - (Instance) and the current state are identified. -- **State Reconciliation**: kro takes necessary actions to align the current - state with the desired state. This may involve creating, updating, or deleting - resources as needed. -- **Status Updates**: The Instance's status is updated to reflect the current - state of reconciliation and any issues encountered. -- **Continuous Loop**: This process repeats regularly, ensuring the cluster - state always converges towards the desired state defined in the Instance. - -## Advantages of declarative management [need better title] - -- **Declarative Management**: Users define what they want, not how to get there. -- **Self-healing**: The system continuously works to maintain the desired state. -- **Idempotency**: The same Instance always results in the same end state, - regardless of the current state. -- **Abstraction**: Complex resource relationships are managed behind the scenes. -- **Consistency**: All resources for an application are managed as a unit. -- **Auditability**: The Instance serves as a single source of truth for the - application's desired state. - -:::tip[Best Practices] +When you create this instance, kro: -- Treat instances as declarative definitions of your application's desired - state. Use version control for your Instances to track changes over time. -- Leverage labels and annotations in Instances for organization and filtering. -- Regularly review Instances to ensure they reflect current requirements. -- Use kro's dry-run feature to preview reconciliation actions before applying - changes to Instances. -- Monitor Instance statuses to understand the current state of your - applications. +- 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 -## Common Status Fields +kro uses the standard Kubernetes reconciliation pattern to manage instances: -kro automatically injects two common fields into the status of all instances: -**Conditions** and **State**. These fields provide crucial information about the -current status of the instance and its associated resources. +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 -### 1. Conditions +This continuous loop ensures your resources stay in sync with your desired +state, providing features like: -Conditions are a standard Kubernetes concept that kro leverages to provide -detailed status information. Each condition represents a specific aspect of the -instance's state. Common conditions include: +- Self-healing +- Automatic updates +- Consistent state management +- Status tracking -- **Ready**: Indicates whether the instance is fully reconciled and operational. -- **Progressing**: Shows if the instance is in the process of reaching the - desired state. -- **Degraded**: Signals that the instance is operational but not functioning - optimally. -- **Error**: Indicates that an error has occurred during reconciliation. +## Monitoring Your Instances -Each condition typically includes the following properties: +KRO provides rich status information for every instance: -- **Type**: The name of the condition (e.g., "Ready"). -- **Status**: Either "True", "False", or "Unknown". -- **LastTransitionTime**: When the condition last changed. -- **Reason**: A brief, machine-readable explanation for the condition's state. -- **Message**: A human-readable description of the condition. +```bash +$ kubectl get webapplication my-app +NAME STATUS SYNCED AGE +my-app ACTIVE true 30s +``` -Example: +For detailed status, check the instance's YAML: ```yaml status: - conditions: + state: ACTIVE # High-level instance state + availableReplicas: 3 # Status from Deployment + conditions: # Detailed status conditions - type: Ready status: "True" lastTransitionTime: "2024-07-23T01:01:59Z" @@ -125,27 +76,42 @@ status: message: "All resources are available and configured correctly" ``` -### 2. State +### Understanding Status -The State field provides a high-level summary of the instance's current status. -It is typically one of the following values: +Every instance includes: -- **Pending**: The instance is being processed, but resources are not yet fully - created or configured. -- **Running**: All resources are created and the instance is operational. -- **Failed**: An error occurred and the instance could not be fully reconciled. -- **Terminating**: The instance is in the process of being deleted. -- **Degraded**: The instance is operational but not functioning optimally. -- **Unknown**: The instance's status cannot be determined. +1. **State**: High-level status -Example: + - `Running`: All resources are ready + - `Progressing`: Working towards desired state + - `Failed`: Error occurred + - `Terminating`: Being deleted -```yaml -status: - state: Running -``` +2. **Conditions**: Detailed status information + + - `Ready`: Instance is fully operational + - `Progressing`: Changes are being applied + - `Degraded`: Operating but not optimal + - `Error`: Problems detected + +3. **Resource Status**: Status from your resources + - Values you defined in your ResourceGroup's status section + - Automatically updated as resources change + +## 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. -These common status fields provide users with a consistent and informative way -to check the health and state of their instances across different -ResourceGroups. They are essential for monitoring, troubleshooting, and -automating operations based on the status of kro-managed resources. +- **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/docs/docs/getting-started/02-deploy-a-resource-group.md b/website/docs/docs/getting-started/02-deploy-a-resource-group.md index b235e0a..2e0827f 100644 --- a/website/docs/docs/getting-started/02-deploy-a-resource-group.md +++ b/website/docs/docs/getting-started/02-deploy-a-resource-group.md @@ -24,15 +24,6 @@ Under the hood, when you create a `ResourceGroup`, kro: 3. Creates a new API (CRD) in your cluster 4. Configures itself to watch and serve instances of this API -:::tip[info] - -**kro** is a Kubernetes-native tool that speaks **Kubernetes**! All you need to -get started is a Kubernetes cluster that supports CRDs, version 1.16 or later. -kro understands native Kubernetes resource as well as any custom resources your -cluster supports. - -::: - ## Prerequisites Before you begin, make sure you have the following: diff --git a/website/docs/docs/getting-started/img/docsVersionDropdown.png b/website/docs/docs/getting-started/img/docsVersionDropdown.png deleted file mode 100644 index 97e4164..0000000 Binary files a/website/docs/docs/getting-started/img/docsVersionDropdown.png and /dev/null differ diff --git a/website/docs/docs/getting-started/img/localeDropdown.png b/website/docs/docs/getting-started/img/localeDropdown.png deleted file mode 100644 index e257edc..0000000 Binary files a/website/docs/docs/getting-started/img/localeDropdown.png and /dev/null differ diff --git a/website/docs/examples/ack-eks-cluster.md b/website/docs/examples/ack-eks-cluster.md new file mode 100644 index 0000000..651fe56 --- /dev/null +++ b/website/docs/examples/ack-eks-cluster.md @@ -0,0 +1,222 @@ +--- +sidebar_position: 10 +--- + +# EKS Cluster + +```yaml title="eks.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGroup +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: + - name: 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 + - name: clusterElasticIPAddress + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: ElasticIPAddress + metadata: + name: kro-cluster-eip + spec: {} + - name: clusterInternetGateway + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: InternetGateway + metadata: + name: kro-cluster-igw + spec: + vpc: ${clusterVPC.status.vpcID} + - name: 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} + - name: 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 + - name: 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 + - name: clusterNATGateway + template: + apiVersion: ec2.services.k8s.aws/v1alpha1 + kind: NATGateway + metadata: + name: kro-cluster-natgateway1 + spec: + subnetID: ${clusterSubnetB.status.subnetID} + allocationID: ${clusterElasticIPAddress.status.allocationID} + - name: 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" + } + ] + } + - name: 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" + } + ] + } + - name: 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" + ] + } + ] + } + - name: 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} + - name: 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/docs/examples/ack-networking-stack.md b/website/docs/examples/ack-networking-stack.md new file mode 100644 index 0000000..b86cc96 --- /dev/null +++ b/website/docs/examples/ack-networking-stack.md @@ -0,0 +1,79 @@ +--- +sidebar_position: 10 +--- + +# Networking Stack + +```yaml title="networking-stack.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGroup +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: + - name: 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 + - name: 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} + - name: 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} + - name: 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} + - name: 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/docs/examples/ack-valkey-cachecluster.md b/website/docs/examples/ack-valkey-cachecluster.md new file mode 100644 index 0000000..38b02a4 --- /dev/null +++ b/website/docs/examples/ack-valkey-cachecluster.md @@ -0,0 +1,76 @@ +--- +sidebar_position: 10 +--- + +# Valkey cluster + +```yaml title="valkey-cachecluster.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGroup +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: + - name: networkingStack + template: + apiVersion: kro.run/v1alpha1 + kind: NetworkingStack + metadata: + name: ${schema.spec.name}-networking-stack + spec: + name: ${schema.spec.name}-networking-stack + - name: 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} + - name: 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 + - name: 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/docs/examples/deploying-controller.md b/website/docs/examples/deploying-controller.md new file mode 100644 index 0000000..084c324 --- /dev/null +++ b/website/docs/examples/deploying-controller.md @@ -0,0 +1,297 @@ +--- +sidebar_position: 10 +--- + +# Controller Deployment + +```yaml title="controller-rg.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGroup +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 deployement 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: + - name: eksCRDGroup + template: + apiVersion: kro.run/v1alpha1 + kind: EKSCRDGroup + metadata: + name: ${schema.spec.name}-crd-group + spec: + name: ${schema.spec.name}-crd-group + - name: 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": "*" + } + ] + } + - name: 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}"} + } + }] + } + - name: 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} + - name: 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 + - name: 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} + - name: 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/docs/examples/deploying-coredns.md b/website/docs/examples/deploying-coredns.md new file mode 100644 index 0000000..aa483f0 --- /dev/null +++ b/website/docs/examples/deploying-coredns.md @@ -0,0 +1,189 @@ +--- +sidebar_position: 10 +--- + +# CoreDNS Deployment + +```yaml title="coredns-rg.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGroup +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: + - name: 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 + - name: 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} + - name: 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 + } + - name: 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 + - name: 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 + - name: 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/docs/examples/ekscluster.md b/website/docs/examples/ekscluster.md deleted file mode 100644 index af85093..0000000 --- a/website/docs/examples/ekscluster.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -sidebar_position: 10 ---- - -# EKSCluster - -```yaml title="ekscluster-rg.yaml" -apiVersion: kro.run/v1alpha1 -kind: ResourceGroup -metadata: - name: kro.run/v1alpha1 -spec: - # CRD Definition - apiVersion: v1alpha1 - kind: EKSCluster - - definition: - spec: - name: string - version: string - numNodes: integer - - # resources - resources: - - name: clusterVPC - definition: - apiVersion: ec2.services.k8s.aws/v1alpha1 - kind: VPC - metadata: - name: cluster-vpc-${schema.spec.name} - spec: - cidrBlocks: - - 192.168.0.0/16 - enableDNSHostnames: false - enableDNSSupport: true - - - name: subnetAZA - definition: - apiVersion: ec2.services.k8s.aws/v1alpha1 - kind: Subnet - metadata: - name: cluster-subnet-a-${schema.spec.name} - spec: - availabilityZone: us-west-2a - cidrBlock: 192.168.0.0/18 - vpcID: ${clusterVPC.status.vpcID} - - - name: securityGroup - definition: - apiVersion: ec2.services.k8s.aws/v1alpha1 - kind: SecurityGroup - metadata: - name: cluster-security-group-${schema.spec.name} - spec: - vpcID: ${clusterVPC.status.vpcID} - name: my-eks-cluster-sg-${schema.spec.name} - description: something something - - - name: subnetAZB - definition: - apiVersion: ec2.services.k8s.aws/v1alpha1 - kind: Subnet - metadata: - name: cluster-subnet-b-${schema.spec.name} - spec: - availabilityZone: us-west-2b - cidrBlock: 192.168.64.0/18 - vpcID: ${clusterVPC.status.vpcID} - - - name: clusterRole - definition: - apiVersion: iam.services.k8s.aws/v1alpha1 - kind: Role - metadata: - name: cluster-role-${schema.spec.name} - spec: - name: cluster-role-${schema.spec.name} - policies: - - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy - assumeRolePolicyDocument: | - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Service": "eks.amazonaws.com" - }, - "Action": "sts:AssumeRole" - } - ] - } - - - name: nodeRole - definition: - apiVersion: iam.services.k8s.aws/v1alpha1 - kind: Role - metadata: - name: cluster-node-role-${schema.spec.name} - spec: - name: cluster-node-role-${schema.spec.name} - 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" - } - ] - } - - - name: cluster - definition: - apiVersion: eks.services.k8s.aws/v1alpha1 - kind: Cluster - metadata: - name: cluster-${schema.spec.name} - spec: - name: cluster-${schema.spec.name} - roleARN: ${clusterRole.status.ackResourceMetadata.arn} - version: ${schema.spec.version} - resourcesVPCConfig: - subnetIDs: - - ${subnetAZA.status.subnetID} - - ${subnetAZB.status.subnetID} - - - name: nodegroup - definition: - apiVersion: eks.services.k8s.aws/v1alpha1 - kind: Nodegroup - metadata: - name: nodegroup-${schema.spec.name} - spec: - name: nodegroup-${schema.spec.name} - clusterName: cluster-${schema.spec.name} - subnets: - - ${subnetAZA.status.subnetID} - - ${subnetAZB.status.subnetID} - nodeRole: ${nodeRole.status.ackResourceMetadata.arn} - updateConfig: - maxUnavailable: 1 - scalingConfig: - minSize: ${schema.spec.numNodes} - maxSize: ${schema.spec.numNodes} - desiredSize: ${schema.spec.numNodes} -``` diff --git a/website/docs/examples/examples.md b/website/docs/examples/examples.md new file mode 100644 index 0000000..299e59b --- /dev/null +++ b/website/docs/examples/examples.md @@ -0,0 +1,49 @@ +--- +sidebar_position: 0 +--- + +# Examples + +This section provides a collection of examples demonstrating how to define and +use ResourceGroups 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 ResourceGroup (Noop)](./noop.md) Explore the simplest form of a + ResourceGroup that doesn't define any resources, serving as a reference for + the basic structure. + +- [Simple Web Application](./web-app.md) Deploy a basic web application with a + Deployment and Service. + +- [Web Application with Ingress](./web-app-ingress.md) Extend the basic web + application example to include an optional Ingress resource for external + access. + +## Advanced Examples + +- [Deploying CoreDNS](./deploying-coredns.md) Learn how to deploy CoreDNS in a + Kubernetes cluster using kro ResourceGroups, including the necessary + Deployment, Service, and ConfigMap. + +- [Deploying a Controller](./deploying-controller.md) Discover how to deploy a + Kubernetes controller using kro ResourceGroups, 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 ResourceGroups, 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 ResourceGroup. + +- [Valkey CacheCluster with ACK CRDs](./ack-valkey-cachecluster.md) Learn how to + create and configure a Valkey CacheCluster using ACK CRDs in a kro + ResourceGroup. + +- [Pod and RDS DBInstance](./pod-rds-dbinstance.md) Deploy a Pod and an RDS + DBInstance in a kro ResourceGroup, showcasing the use of multiple resources + with dependencies. diff --git a/website/docs/examples/empty.md b/website/docs/examples/noop.md similarity index 76% rename from website/docs/examples/empty.md rename to website/docs/examples/noop.md index 26d9362..81da2c0 100644 --- a/website/docs/examples/empty.md +++ b/website/docs/examples/noop.md @@ -1,17 +1,17 @@ --- -sidebar_position: 0 +sidebar_position: 5 --- # Empty ResourceGroup -```yaml title="no-resources-rg.yaml" +```yaml title="noop.yaml" apiVersion: kro.run/v1alpha1 kind: ResourceGroup metadata: name: kro.run/v1alpha1 spec: apiVersion: v1alpha1 - kind: Noop + kind: NoOp definition: spec: name: string | required=true diff --git a/website/docs/examples/deploymentdbinstance.md b/website/docs/examples/pod-rds-dbinstance.md similarity index 97% rename from website/docs/examples/deploymentdbinstance.md rename to website/docs/examples/pod-rds-dbinstance.md index 88073c3..e200f72 100644 --- a/website/docs/examples/deploymentdbinstance.md +++ b/website/docs/examples/pod-rds-dbinstance.md @@ -2,7 +2,7 @@ sidebar_position: 20 --- -# DeploymentDBInstance +# Pod with RDS DBInstance ```yaml title="deploymentdbinstance-rg.yaml" apiVersion: kro.run/v1alpha1 diff --git a/website/docs/examples/web-app-ingress.md b/website/docs/examples/web-app-ingress.md new file mode 100644 index 0000000..4113aed --- /dev/null +++ b/website/docs/examples/web-app-ingress.md @@ -0,0 +1,92 @@ +--- +sidebar_position: 10 +--- + +# Web Application w/ Ingress + +```yaml title="webapp-ingress.yaml" +apiVersion: kro.run/v1alpha1 +kind: ResourceGroup +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 RG (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: + - name: 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 + + - name: 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 + + - name: 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/docs/examples/deploymentservice.md b/website/docs/examples/web-app.md similarity index 96% rename from website/docs/examples/deploymentservice.md rename to website/docs/examples/web-app.md index 56291d5..d8ee2b7 100644 --- a/website/docs/examples/deploymentservice.md +++ b/website/docs/examples/web-app.md @@ -1,8 +1,8 @@ --- -sidebar_position: 1 +sidebar_position: 10 --- -# DeploymentService +# Web Application ```yaml title="deploymentservice-rg.yaml" apiVersion: kro.run/v1alpha1 diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 6424798..4d02abd 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -90,12 +90,12 @@ const config: Config = { position: "left", label: "Examples", }, - { + /* { type: "docSidebar", sidebarId: "apisSidebar", position: "left", label: "API Reference", - }, + }, */ { type: "docsVersionDropdown", position: "right",