Krateo OASGen Provider
The Krateo OASGen Provider is a Kubernetes controller that generates Custom Resource Definitions (CRDs) and controllers to manage resources directly from OpenAPI Specification (OAS) 3.0/3.1 documents. It enables seamless integration of API-defined resources into Kubernetes environments.
Summary
- Requirements
- How to Install
- Glossary
- Why OASGen Provider?
- Architecture
- Workflow
- RestDefinition
- Authentication
- Configuration resources
- Usage guide
- Environment Variables and Flags
- Security Features
- Best Practices
- Unsupported features
Requirements
- Kubernetes cluster (v1.20+ recommended).
- OpenAPI Specification 3.0/3.1 documents for the APIs you want to manage. Please refer to the Usage Guide section for more details on the OAS document management.
- Network access to API endpoints from the cluster.
How to Install
Install OASGen Provider by installing the related Helm chart:
helm repo add krateo https://charts.krateo.io
helm repo update
helm install oasgen-provider krateo/oasgen-provider --namespace krateo-system
Glossary
Term | Definition |
---|---|
CRD (Custom Resource Definition) | A Kubernetes resource that defines custom objects and their schemas. |
CR (Custom Resource) | An instance of a Custom Resource Definition (CRD) |
RestDefinition | A Custom Resource managed by oasgen-provider that defines how API resources are managed in Kubernetes based on OAS specifications. |
RDC (Rest Dynamic Controller) | A controller deployed by the oasgen-provider to manage Custom Resources representing external resources defined by a RestDefinition. |
OAS (OpenAPI Specification) | A standard, language-agnostic interface description for REST APIs (https://www.openapis.org/). |
Resource CRD | A Custom Resource Definition generated by oasgen-provider based on the OAS schema. |
External system / External APIs | The service or API that we want to manage through Kubernetes resources. |
Resource reconciliation | The process of ensuring that the state of resources in the external system matches the desired state defined in Kubernetes resources which is the source of truth. |
Plugin | An optional wrapper web service to implement to maintain consistent API interfaces when needed. Can be also viewed as an adapter pattern / proxy service since it add a layer of indirection between the Rest Dynamic Controller and the external API. |
Why OASGen Provider?
The OASGen Provider aims to reduce the complexity and effort required to manage external APIs in Kubernetes environments normally experienced with traditional custom operators. Instead of writing and maintaining custom operators for each API, OASGen leverages the OAS definition to automatically generate CRDs and controllers, making any OpenAPI-compliant API manageable as a first-class Kubernetes resource.
Key Advantages
-
No operator boilerplate
- Traditional operators require writing controllers, CRDs, RBACs, and more manually.
- OASGen eliminates this burden: from a single
RestDefinition
manifest pointing to an OAS file, it generates CRDs and a controller (Rest Dynamic Controller
) ready to reconcile resources.
-
OAS as the single source of truth
- By consuming the OpenAPI Specification, OASGen ensures type safety, schema validation, and consistency directly from the API contract.
- Resource definitions in Kubernetes always match the schema of the underlying API.
-
Pluggable architecture for inconsistent APIs
- Not all APIs are consistent in their identifiers, response bodies, or field naming.
- OASGen supports Plugins (Wrapper Web Services) to normalize such APIs without changing the user experience in Kubernetes.
-
Extensibility with zero-code changes
- To add support for a new external API, you don’t need to write Go code or recompile anything.
- Just supply an OAS document and author a
RestDefinition
. - OASGen takes care of generating the CRD and deploying the controller.
-
Authentication and configuration management
- OASGen automatically generates configuration CRDs that handle authentication and configuration parameters.
- Credentials are securely managed via Kubernetes secrets.
Comparison with Traditional Operator approach
Aspect | Traditional Operator | OASGen Provider |
---|---|---|
Development Effort | Usually requires writing Go code, CRDs, RBAC, testing, and lifecycle management. | Zero code – generate CRDs/controllers directly from OAS + RestDefinition . |
Schema Definition | Must be manually translated into Kubernetes CRDs. | Automatically inferred from OAS schema. |
Reconciliation Logic | Custom-written per operator. | Generic, auto-generated reconciliation (create, update, delete, observe). |
Consistency with API | High risk of schema drift if the API evolves. | Always matches OAS contract (type-safe CRDs + validation). |
Handling API Inconsistencies | Requires patching code in the operator. | Use an optional Plugin (Wrapper Web Service) without modifying the core provider. |
Extensibility | New APIs require new operators or significant code changes. | New APIs supported by simply adding a new RestDefinition with an OAS file. |
Security & Configuration | Custom implementation per operator. | Auto-generated *Configuration CRDs integrated with Kubernetes secrets. |
Blast Radius on errors | Bugs in custom code can lead to crashes or misbehavior. A bug for Resource A can affect Resources B, C and D if in the same operator. | Isolated controllers per RestDefinition . Bugs are contained; one faulty API does not affect others. Generic controllers with well-defined behavior; less custom code means fewer bugs. |
Architecture
In the following diagrams, we illustrate the architecture and workflow of the OASGen Provider in two scenarios: a standard scenario and a scenario that includes an optional Plugin (Wrapper Web Service).
Standard scenario
flowchart LR
subgraph Generator
provider[oasgen-provider]
restdefinition[[RestDefinition Manifest]]
crd[[CRD Manifest]]
end
subgraph Dynamic_Controller["Dynamic Controller"]
rdc[rest-dynamic-controller]
cr[[Custom resource Manifest]]
end
er[(External Resource)]
restdefinition -.->|Definition for| provider
provider -->|Generate| crd
provider -->|Deploy| rdc
cr -.->|Definition for| rdc
rdc -->|"Manage (Observe, Create, Update, Delete)"| er
cr -.->|Instance of| crd
The diagram illustrates how the OASGen Provider processes OpenAPI Specifications to generate CRDs and deploy the Rest Dynamic Controller (RDC). The RDC manages custom resources and interacts with external APIs.
Scenario with Plugin (Wrapper Web Service)
flowchart LR
subgraph Generator
provider[oasgen-provider]
restdefinition[[RestDefinition Manifest]]
crd[[CRD Manifest]]
end
subgraph Dynamic_Controller["Dynamic Controller"]
rdc[rest-dynamic-controller]
cr[[Custom resource Manifest]]
end
er[(External Resource)]
restdefinition -.->|Definition for| provider
provider -->|Generate| crd
provider -->|Deploy| rdc
cr -.->|Definition for| rdc
rdc --> |"Manage (Observe, Create, Update, Delete)"| ws[Plugin <br>Wrapper Web Service]
ws --> er
cr -.->|Instance of| crd
In this secondo scenario, very similar to the first one, the Rest Dynamic Controller interacts with an optional Plugin (Wrapper Web Service) to handle API calls. This is useful when the external API does not conform to the expected interface or requires additional processing.
Workflow
Standard scenario
- User applies a RestDefinition CR.
oasgen-provider
fetches the OAS specification.oasgen-provider
generates a CRD based on the OAS schema.oasgen-provider
deploys the specific Rest Dynamic Controller.- Rest Dynamic Controller manages custom resources according to API specifications.
- Resources states are synchronized with external APIs (observe, create, update, delete).
Scenario with Plugin (Wrapper Web Service)
- User applies a RestDefinition CR.
oasgen-provider
fetches the OAS specification.oasgen-provider
generates a CRD based on the OAS schema.oasgen-provider
deploys the specific Rest Dynamic Controller.- Rest Dynamic Controller manages custom resources according to API specifications.
- Resources states are synchronized with external APIs (observe, create, update, delete) through the Plugin (Wrapper Web Service) as an intermediary.
RestDefinition
The RestDefinition
is the core Custom Resource Definition (CRD) used by the OASGen Provider to define how external API resources are managed in Kubernetes based on OpenAPI Specifications (OAS) 3.0/3.1 documents.
CRD Specification
The RestDefinition CRD specification can be found here: RestDefinition CRD.
How to create a RestDefinition
This section shows how to author and apply a RestDefinition
and provides a field-by-field reference extracted from the related CRD schema.
Prerequisites
-
Have an OpenAPI 3.0/3.1 spec reachable either via:
configmap://<namespace>/<name>/<key>
http(s)://<url>
The
spec.oasPath
field must match one of these forms (see the regex below). Be aware you can changeoasPath
over time but avoid changing the request body of thecreate
action or its parameters when you do so. Otherwise, you most likely need to delete/recreate the RestDefinition in order to avoid CRD/schema drift.
Minimal example
apiVersion: ogen.krateo.io/v1alpha1
kind: RestDefinition
metadata:
name: my-resource
namespace: default
spec:
# 1) Where the OAS file is
oasPath: https://example.com/openapi.yaml
# 2) Group for generated resources
resourceGroup: example.ogen.krateo.io
# 3) What to manage and how
resource:
kind: Widget
# optional, used by the findby action to locate a resource. Important: choose identifiers that are unique per resource.
identifiers:
- name
# optional, adds more fields to status (e.g., technical IDs usually used in get action, and others fields you want to see in status that are returned in the response body of the API)
additionalStatusFields:
- id
- revision
- createdAt
# optional, fields to exclude from spec (e.g., server-generated technical IDs you don't want users to set)
excludedSpecFields:
- id
# optional, declare configuration params to expose in the generated Configuration CRD (Authentication is always included if needed)
configurationFields:
- fromOpenAPI:
name: api-version
in: query
fromRestDefinition:
actions: ["*"] # apply to all actions
- fromOpenAPI:
name: per_page
in: query
fromRestDefinition:
actions:
- get
- findby
# REQUIRED: map CRUD/find operations to HTTP endpoints
verbsDescription:
- action: findby
method: GET
path: /widgets
- action: get
method: GET
path: /widgets/{id}
- action: create
method: POST
path: /widgets
- action: update
method: PATCH
path: /widgets/{id}
- action: delete
method: DELETE
path: /widgets/{id}
Apply and verify:
kubectl apply -f sample-restdefinition.yaml
kubectl get restdefinitions
The CRD exposes columns like READY
, AGE
, API VERSION
, KIND
, and OAS PATH
to quickly inspect state.
Field reference
All paths are relative to spec.*
unless stated otherwise. The “Immutable” column reflects Kubernetes validation rules; immutable fields require deleting/recreating the RestDefinition CR if you need to change them.
The content of this table is derived from the CRD’s OpenAPI schema.
Field (YAML path) | Type | Required | Immutable | Description | Constraints / Notes |
---|---|---|---|---|---|
oasPath | string | ✔︎ | ✖︎ | Path to the OpenAPI specification. | |
resourceGroup | string | ✔︎ | ✔︎ | API group of the generated resource(s). | Changing is rejected by validation. |
resource | object | ✔︎ | ✖︎ | Container for resource mapping and options. | |
resource.kind | string | ✔︎ | ✔︎ | Name (Kind) of the resource to manage (generated CRD Kind). | Changing is rejected by validation. |
resource.verbsDescription[] | array<object> | ✔︎ | ✖︎ | List of actions that the controller will execute. Each item is a single action mapping. | Must include at least the actions you plan to use in reconciliation. |
resource.verbsDescription[].action | string (enum) | ✔︎ | — | Action name. | One of: create , update , get , delete , findby . |
resource.verbsDescription[].method | string (enum) | ✔︎ | — | HTTP method to call. | One of: GET , POST , PUT , DELETE , PATCH . |
resource.verbsDescription[].path | string | ✔︎ | — | HTTP path for the endpoint; must exist in the referenced OAS. | Should exactly match the OAS path you mapped. |
resource.identifiers[] | array<string> | ✖︎ | ✔︎ | Fields used to uniquely identify a resource for findby and are written in status. | Immutable once generated. It is important to choose identifiers that are unique per resource. If findby is not present use just additionalStatusFields and not identifiers . |
resource.additionalStatusFields[] | array<string> | ✖︎ | ✔︎ | Extra fields to expose in status (e.g., technical IDs like id , uuid but also others returned by the API you want to see in status). Usually some of these are used in the get action. | Immutable once generated. |
resource.excludedSpecFields[] | array<string> | ✖︎ | ✔︎ | Fields to exclude from spec (e.g., server-generated technical IDs you don't want users to set). | Immutable once generated. |
resource.configurationFields[] | array<object> | ✖︎ | ✔︎ | Declares configuration parameters in the generated *Configuration CRD. | Immutable once generated. Authentication is always included if needed (you do not need to specify it here). |
resource.configurationFields[].fromOpenAPI.in | string | ✔︎ | — | Location of the parameter in the OAS. | Could be query , path , header , cookie etc. |
resource.configurationFields[].fromOpenAPI.name | string | ✔︎ | — | Parameter name as defined in the OAS. | Must match the OAS exactly. |
resource.configurationFields[].fromRestDefinition.actions[] | array<string> | ✔︎ | — | Which actions the parameter applies to. | ["*"] applies to all defined actions; at least 1 item is required. |
Required top-level fields: spec.oasPath
, spec.resourceGroup
, and spec.resource
.
Within spec.resource
, kind
and verbsDescription
are mandatory.
Validation & mutability highlights:
resourceGroup
,resource.kind
,resource.identifiers
,resource.additionalStatusFields
,resource.excludedSpecFields
, andresource.configurationFields
are immutable (Kubernetes validation enforcesself == oldSelf
). Plan carefully before applying.verbsDescription[].action
/method
are enum-restricted;path
must point to an endpoint present in your OAS.oasPath
accepts eitherconfigmap://...
orhttp(s)://...
and can be updated over time; keep thecreate
request body and parameters stable to avoid CRD/schema drift. Otherwise, you may need to delete/recreate the RestDefinition.
Tips and best practices for RestDefinition authoring
- Start with both
findby
andget
actions where applicable:findby
to locate a resource using human-friendly identifiers,get
for subsequent, efficient lookups using technical IDs. (See the dedicated action sections below: actionfindby
and actionget
.) - Keep
identifiers
small, unique, and human-friendly (e.g., name, email), and place technical IDs (e.g., id, uuid) underadditionalStatusFields
. It is important to choose identifiers that are unique per resource. - Use
excludedSpecFields
to avoid putting server-generated fields in spec (e.g.,id
). Usually we want to avoid users setting these fields but rather have them in status. - Use
configurationFields
to expose cookies, headers, query and path parameters (e.g.,api-version
) across specific actions with a specific array or all actions with ["*"]. It is duty of the platform engineer to decide whether a parameter should be considered a configuration parameter rather than an application parameter.
Regex and enums (for reference)
resource.oasPath
regex:^(configmap:\/\/([a-z0-9-]+)\/([a-z0-9-]+)\/([a-zA-Z0-9.-_]+)|https?:\/\/\S+)$
- Allowed
verbsDescription[].action
values:create
,update
,get
,delete
,findby
- Allowed HTTP
verbsDescription[].method
values:GET
,POST
,PUT
,DELETE
,PATCH
About Resource reconciliation and RestDefinition Actions
Krateo controllers (among which rest-dynamic-controller
) support 4 verbs to provide resource reconciliation:
- Observe: This verb observes the resource state in the external system. It fetches the current state of the resource. If the resource does not exist or differs from the desired state, the controller will create or update it accordingly.
- Create: This verb creates a new resource in the external system.
- Update: This verb updates an existing resource in the external system.
- Delete: This verb deletes a resource from the external system.
OASGen Provider defines and supports 5 actions: findby
, get
, create
, update
, and delete
.
These actions are used by the rest-dynamic-controller
to actually implement the above verbs for the resource reconciliation process:
Verb | Action(s) defined by oasgen-provider and rest-dynamic-controller | Description |
---|---|---|
Observe | findby , get | Fetches the current state of the resource from the external system. |
Create | create | Creates a new resource in the external system. |
Update | update | Updates an existing resource in the external system. |
Delete | delete | Deletes a resource from the external system. |
It must be noted that the source of truth is the resource applied to the Kubernetes cluster, and not the resource in the external system.
Therefore, the rest-dynamic-controller
will always try to make the external system match the desired state defined in the resource manifest applied to the Kubernetes cluster.
The Kubernetes reconcilation loop can be summarized (at a high-level) with the following diagram with the actions defined by oasgen-provider
and rest-dynamic-controller
:
graph LR
subgraph K8s[Kubernetes Cluster]
A[Declared <br> desired state]
end
subgraph EXT[External System]
B[Observed <br> current state]
end
A -->|**Act** <br> create <br> update <br> delete| B
A -->|**Observe** <br> findby <br> get| B
Action findby
The findby
action finds a resource using its identifiers, which are defined as a list of fields in the RestDefinition manifest.
The API endpoint used for this action must return a list or collection of resources, from which the rest-dynamic-controller
will select the matching resource.
Purpose of Identifiers
Identifiers are fields that uniquely identify a resource within the external system.
Unlike the so-called "technical keys" or "technical identifiers" such as id
or uuid
, in the context of findby
, these identifiers are "human-friendly" and typically available before a resource is created:
- Examples:
name
,email
,title
- Characteristics: unique per resource, usually string-based, user-facing
Note that it is the responsibility of the user to ensure that the identifiers defined in the RestDefinition manifest are indeed unique within the external system.
For instance, if name
is used as an identifier, the user must ensure that no two resources can have the same name
in the external system in the context of the resource being managed.
As an example, in the context of a Email service, email
is a good candidate for an identifier because it is unique per user. Even if the external system uses a unique technical identifier (like id
or uuid
) as the primary key for users, email
can still be used as an identifier for the findby
action because it is unique per user and available before the user is created.
Why Use findby
?
The findby
action is commonly used by the Observe verb during the first step of the reconciliation process.
When a resource manifest is applied to the Kubernetes cluster for the first time, the rest-dynamic-controller
must check whether the resource already exists in the external system. At this stage:
- The resource’s external identifier (id, uuid, etc.) is not yet available in the resource manifest.
- These identifiers are typically generated by the external system upon creation.
Because of this, usually the rest-dynamic-controller
cannot use the get
action, which typically requires a unique technical identifier (id, uuid, etc.) to fetch a single resource.
The HTTP GET endpoint for the get
action would look like this:
GET /resources/{id}
Where {id}
is a unique technical identifier that is not known before the resource is created but it is generated by the external system.
In these cases, rest-dynamic-controller
should rely on findby
to search for the resource using the human-friendly identifiers that are available upfront.
When the findby
action is not needed
The findby
action is not needed when the external system uses a human-friendly unique identifier (like name
or email
) as the primary key for resources, and this identifier is available before the resource is created.
Therefore, the HTTP GET endpoint would look like this:
GET /resources/{name}
In such cases, the get
action can be used directly to fetch the resource from the external system even during the first ever reconciliation step.
Why not only set findby
and not get
?
In theory, rest-dynamic-controller
could work with just the findby
action and without the get
action, however there is the major drawback that the findby
action returns a list or collection of resources.
This lists can be very large, and fetching and processing them can be inefficient and slow.
For this reason, it is strongly recommended to always define both findby
and get
actions in the RestDefinition manifest, whenever possible and meaningful.
Action get
The get
action retrieves the current state of a resource which typically means fetching a single resource by a unique identifier.
Differently from findby
, the API endpoint used for this action must return a single resource.
As mentioned in the findby
section, the nature of the identifiers vary depending on the external system.
"Technical Identifiers" (typically get ) | "Human-friendly Identifiers" (typically findby ) |
---|---|
id | name |
uuid | |
resourceId | title |
In the majority of cases, the get
and the findby
actions use different identifiers:
findby
uses "human-friendly" identifiers that are typically available before the resource is created (e.g.,name
,email
,title
).get
uses "technical identifiers" that are typically generated by the external system upon resource creation (e.g.,id
,uuid
,resourceId
).
Therefore the get
action is typically used by the Observe verb during subsequent reconciliation loops (after the first one onward), after the resource has been created and the unique technical identifier (id, uuid, etc.) is known and stored in the resource status.
Note: since the get
action usually uses a unique technical identifier (id, uuid, etc.) that is generated by the external system upon resource creation, it is useful to use the field excludedSpecFields
in the RestDefinition manifest to exclude these fields from the resource spec, so that the generated CRD does not allow users to set these fields in the resource spec. These fields should be set by the controller in the resource status only.
When the get
action is sufficient
There are cases however where only the get
action is sufficient for the Observe verb, for example when the external system uses a human-friendly unique identifier like name
or email
as the primary key for resources, and this identifier is available before the resource is created.
Therefore, the HTTP GET endpoint would look like this:
GET /resources/{name}
In such cases, the get
action can be used directly to fetch the resource from the external system even during the first ever reconciliation step.
An example of this is the GitHub API, in the context of repositories, the name
is unique per user/organization and it is available before the repository is created, therefore the get
action can be used directly to fetch a repository by its name
. For example:
GET /repos/{org}/{repo}
Where {repo}
is the repository name, which is unique per user/organization and it is available before the repository is created (it is provided by the user when creating the repository).
Typical complete workflow with both findby
and get
Assumptions:
- The external system uses a unique technical identifier (id, uuid, etc.) as the primary key for resources.
- The unique technical identifier (id, uuid, etc.) is generated by the external system upon resource creation and therefore not known before the resource is created.
- The RestDefinition manifest defines both
findby
andget
actions. - The HTTP GET endpoint assigned to the
get
action looks like this:
GET /resources/{id}
Where {id}
is a unique technical identifier that is not known before the resource is created but it is generated by the external system.
- The HTTP GET endpoint assigned to the
findby
action looks like this:
GET /resources
Having defined the above assumptions, here is a typical complete workflow, assuming resource does not exist yet in the external system, would be:
- The user applies a resource manifest to the Kubernetes cluster.
- The
rest-dynamic-controller
uses thefindby
action to search for the resource using the human-friendly identifiers. - If the resource is not found, the
rest-dynamic-controller
uses thecreate
action to create the resource in the external system. - The
status
of the resource is populated with the unique technical identifier (id, uuid, etc.) returned by thecreate
action. - In subsequent reconciliation loops, the
rest-dynamic-controller
uses theget
action with the unique technical identifier to fetch the resource.
Action create
The create
action creates a new resource in the external system.
The endpoint used for this action must accept a request body containing the resource data in the format defined by the OAS schema.
The request body should be strongly typed to match the OAS schema, ensuring data validation before being sent to the external system.
The request body is used by oasgen-provider
to generate the Resource CRD.
Typically the external system would return a 201 Created
response upon successful resource creation, along with the details of the newly created resource in the response body.
The rest-dynamic-controller
will then update the Custom Resource (CR) status with the values returned in the response body.
It is important to note that some APIs may have asynchronous resource creation processes.
Therefore the external system may respond with a 202 Accepted
response, indicating that the resource creation is in progress.
In this case, the rest-dynamic-controller
will put the Custom Resource (CR) into a Pending
state with Ready=False
, and the controller will continue to monitor the resource until it is fully created (the get
or findby
does not return 404
).
Once the resource is created, the controller will update the CR status to Ready=True
.
Action update
The update
action updates an existing resource in the external system.
The endpoint used for this action must use as request body a subset of the fields defined in the OAS schema for the resource creation, containing only the fields that need to be updated.
The subset could be the entire resource schema, or just a few fields, depending on the API capabilities.
It must be noted that it can have fewer fields, but not more, because the CRD is generated from the create
action request body.
Action delete
The delete
action removes a resource from the external system.
The endpoint used for this action must accept the unique identifier of the resource to be deleted, typically provided as a path parameter.
After a successful delete, the rest-dynamic-controller expects that any subsequent call to the findby or get actions will not return the deleted resource.
The delete action must also be idempotent: if the resource does not exist in the external system, the endpoint should return a success response rather than an error. This behavior ensures that reconciliation can proceed correctly, since the desired state (“resource absent”) already matches the actual state.
The typical status code for a successful delete operation is 204 No Content
, but 200 OK
or 202 Accepted
(and all successful status codes) are also acceptable.
API Endpoints consistency requirements
Some consistency requirements includes but may not be limited to:
- Field names must be consistent across all actions (
create
,update
,findby
,get
,delete
) - API responses must be consistent with the fields of the resource schema defined in the OAS document and so the CRD schema.
- Path parameters and request / response body fields should use consistent naming (e.g.,
userId
vsuser_id
is not consistent, also havingrepositoryId
as path paramter andid
in the response body is not consistent).
Any API behavior that does not match these requirements will require a web service wrapper to normalize / fix the API interface. This is common with APIs that do not follow consistent naming conventions or have different response structures. To learn more about web service wrappers, please refer to the usage guide.
Type-Safe Status Fields
The OASGen Provider automatically generates a status
subresource for the generated resource CRD, providing visibility into the state of the external resource.
The fields within the status are derived from two sources in your RestDefinition
:
identifiers
: Fields used to uniquely identify the resource with afindby
action.additionalStatusFields
: Any other fields you wish to expose in the status. So also technical identifiers likeid
,uuid
, etc. can be added here and can be used in theget
action.
To ensure type safety, the oasgen-provider
inspects the response schema of the get
(or findby
as a fallback) action in your OpenAPI specification. It uses the types defined in the OAS to generate the corresponding fields in the CRD's status schema.
String Fallback Mechanism in Status Fields
When the provider cannot find a specified identifier
or additionalStatusField
in the OAS response schema, it employs a string fallback mechanism for that status fields:
- The OASGen Provider logs a warning indicating that the field was not found in the OAS response.
- It generates that specific status field with
type: string
as a safe default.
Kubernetes API Server Validation example
It must be noted that the Kubernetes API server performs strict validation of the resource status against the CRD schema.
For example, if the response schema of an hypothetical update
action returns as a response body a field with a type different than the one defined in the CRD, the Kubernetes API server will reject the update request:
- The controller receives the response body from the external API.
- The controller sends the update request for the custom resource's status to the Kubernetes API server.
- The API server receives the request and validates it against the CRD's schema.
- It sees that the update is trying to put a string ("123") into the
status.revision
field. - It checks the schema and sees that
status.revision
must be an integer. - The validation fails, and the Kubernetes API server rejects the entire status update.
If you check the status of the custom resource, you will see that the status.conditions
field contains an error message indicating that the update failed due to a schema validation error, like the following:
status:
conditions:
- lastTransitionTime: "2025-07-22T12:45:38Z"
message: ""
reason: Creating
status: "False"
type: Ready
- lastTransitionTime: "2025-07-22T12:53:43Z"
message: 'observe failed: Pipeline.azuredevops.kog.krateo.io "test-pipeline-kog-1"
is invalid: [status.id: Invalid value: "string": id in body must be of type
integer: "string", status.revision: Invalid value: "string": revision in body
must be of type integer: "string"]'
reason: ReconcileError
status: "False"
type: Synced
Authentication
The OASGen Provider currently supports 2 authentication mechanisms to connect to external APIs:
- Bearer Token: This method uses a bearer token for authentication.
- Basic Authentication: This method uses basic authentication with a username and password.
This means that the external API must support one of these authentication methods to be compatible with the OASGen Provider.
The authentication method must be defined in the OpenAPI Specification (OAS) document using the securitySchemes
component as better explained in the Usage Guide.
The authentication details are stored in the generated configuration resource, which is used by the rest-dynamic-controller
to connect to the external API.
The authentication details are referenced in the configuration resource using Kubernetes secrets.
The section Configuration resources below provides more details and examples.
Configuration resources
OASGen provider generates a configuration resource for each RestDefinition created. This configuration resource is used to store configuration and authentication details needed to connect to the external API.
For example, if a RestDefinition is created to manage a GitHub Repo resource, the generated resources will be:
Kind | ApiVersion |
---|---|
Repo | github.ogen.krateo.io/v1alpha1 |
RepoConfiguration | github.ogen.krateo.io/v1alpha1 |
By default, the generated configuration resource will include at least the authentication details needed to connect to the external API.
For instance, in the case of GitHub, the configuration resource of the Repo resource can be configured as follows:
apiVersion: github.ogen.krateo.io/v1alpha1
kind: RepoConfiguration
metadata:
name: my-repo-config
namespace: default
spec:
authentication:
bearer:
# Reference to a secret containing the bearer token
tokenRef:
name: gh-token # Name of the secret
namespace: default # Namespace where the secret exists
key: token # Key within the secret that contains the token
A more complex example of configuration is the Azure DevOps Pipeline resource, which contains configuration fields along with authentication details:
apiVersion: azuredevops.ogen.krateo.io/v1alpha1
kind: PipelineConfiguration
metadata:
name: my-pipeline-config
namespace: default
spec:
authentication:
basic:
usernameRef:
name: azuredevops-secret
namespace: default
key: token
passwordRef:
name: azuredevops-secret
namespace: default
key: token
configuration:
query:
create:
api-version: "7.2-preview.1"
delete:
api-version: "7.2-preview.1"
findby:
api-version: "7.2-preview.1"
get:
api-version: "7.2-preview.1"
update:
api-version: "7.2-preview.1"
The RestDefinition manifest must list which configuration fields are needed for the resource to work properly.
The field spec.resource.configurationFields
in the RestDefinition manifest is used to define the configuration fields that will be included in the generated configuration resource.
An example of this field in a RestDefinition manifest is as follows:
configurationFields:
- fromOpenAPI:
name: api-version
in: query
fromRestDefinition:
actions: ["*"] # star means all actions set in the verbsDescription above
Usage guide
A more practical, step-by-step, usage guide with examples and troubleshooting tips can be found in the Usage Guide.
Environment Variables and Flags
Name | Description | Default Value | Notes |
---|---|---|---|
OASGEN_PROVIDER_DEBUG | Enables debug logging | false | Use --debug flag |
OASGEN_PROVIDER_SYNC | Sync period for controller manager | 1h | Duration |
OASGEN_PROVIDER_POLL_INTERVAL | Poll interval for resource drift checks | 5m | Duration |
OASGEN_PROVIDER_MAX_RECONCILE_RATE | The number of concurrent reconciles for each controller. This is the maximum number of resources that can be reconciled at the same time. | 3 | Integer |
OASGEN_PROVIDER_LEADER_ELECTION | Enables leader election for controller manager | false | Use --leader-election flag |
OASGEN_PROVIDER_MAX_ERROR_RETRY_INTERVAL | Maximum retry interval on errors | 1m | Duration |
OASGEN_PROVIDER_MIN_ERROR_RETRY_INTERVAL | Minimum retry interval on errors | 1s | Duration |
Security Features
The OASGen Provider incorporates several security features, at different levels, to ensure safe operation within a Kubernetes environment:
- Automatic generation of Kubernetes RBAC policies for custom resources
- Secure credential management through Kubernetes secrets
Additionally, whenever needed you can leverage custom web service wrappers for additional security layers if needed. For instance, you can use a web service wrapper to add request signing, or other security mechanisms that are not natively supported by the OASGen Provider.
Best Practices
To ensure optimal performance and reliability when using the OASGen Provider, consider the following best practices:
- Always use only OAS 3.0+ specifications as lower versions are not supported.
- Maintain consistent field naming across API endpoints.
- Use web service wrappers when API interfaces are inconsistent or additional processing is needed.
- Regularly update OAS documents to match API changes.
- Monitor controller logs with the
krateo.io/connector-verbose: "true"
annotation added to the CR of the resource you want to monitor.
Unsupported features
Currently, the following OAS features are not supported by OASGen Provider:
number
type is not supported. Useinteger
orstring
instead. During the generation of the CRD schema, if anumber
type is encountered, it will be converted tointeger
. Validation errors (during CRD generation) may happen if the API returns anumber
in response bodies for other actions (e.g.,create
,update
, etc.). Rest Dynamic Controller will perform a similar conversion when dealing with response bodies.nullable
is not supported.nullable
was removed in OAS 3.1 in favor of usingnull
type in the arraytype
.anyOf
,oneOf
,not
are not supported.additionalProperties
is supported only in the boolean form (i.e.,additionalProperties: true
). IfadditionalProperties
is an object, it is not supported.format
is not supported: if OASGen Provider encounters aformat
field, it will simply append it into the description of the field as a note, but it will not use it to generate a more specific type.minItems
,maxItems
,minLength
,maxLength
,minimum
,maximum
,exclusiveMinimum
,exclusiveMaximum
, andpattern
are not supported.readOnly
andwriteOnly
are not supported.
Note that this list may not be exhaustive and other features may also be unsupported.
OAS 3.0 vs OAS 3.1
For a reference to the differences between OAS 3.0 and OAS 3.1, please check the official documentation: https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0