--- slug: cue title: CUE description: Render component manifests directly from CUE. sidebar_position: 50 --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; # CUE ## Overview This tutorial demonstrates how to add additional resources to a component using CUE. Holos components often mix in resources, eliminating the need to modify existing charts or manifests. In this tutorial, we'll add an [ExternalSecret] resource to the podinfo Helm chart configured in the [Hello Holos] tutorial. Key concepts: 1. Resources are validated against `#Resources` defined at the root. 2. Custom Resource Definitions need to be imported using Timoni. 3. Helm, Kustomize, and CUE can be mixed together within the same component. ## The Code ### Generating the Structure Use `holos` to generate a minimal platform directory structure. First, create and navigate into a blank directory. Then, use the `holos generate platform` command to generate a minimal platform. ```shell mkdir holos-cue-tutorial && cd holos-cue-tutorial holos init platform v1alpha5 ``` ### Creating the Component Create the directory for the `podinfo` component. Create an empty file, then add the following CUE configuration to it. ```bash mkdir -p components/podinfo ``` ```bash cat < components/podinfo/podinfo.cue ``` ```cue showLineNumbers package holos // export the component build plan to holos holos: Component.BuildPlan // Component is a Helm chart Component: #Helm & { Name: "podinfo" Namespace: "default" // Add metadata.namespace to all resources with kustomize. KustomizeConfig: Kustomization: namespace: Namespace Chart: { version: "6.6.2" repository: { name: "podinfo" url: "https://stefanprodan.github.io/podinfo" } } } ``` ```bash EOF ``` Register the component with the platform. ```bash cat < platform/podinfo.cue ``` ```cue showLineNumbers package holos Platform: Components: podinfo: { name: "podinfo" path: "components/podinfo" } ``` ```bash EOF ``` Render the platform. ```bash holos render platform ``` ``` cached podinfo 6.6.2 rendered podinfo in 1.938665041s rendered platform in 1.938759417s ``` Add and commit the initial configuration. ```bash git init . && git add . && git commit -m initial ``` ### Mixing in Resources We use the [ComponentConfig] `Resources` field to mix in resources to any component kind. This field is a convenient wrapper around the core [BuildPlan] [Resources] [Generator]. Create the mixins.cue file. ```bash cat < components/podinfo/mixins.cue ``` ```cue showLineNumbers package holos // Component fields are unified with podinfo.cue Component: { // Concrete values are defined in podinfo.cue Name: string Namespace: string // Resources represents mix-in resources organized as a struct. Resources: ExternalSecret: (Name): { // Name is consistent with the component name. metadata: name: Name // Namespace is consistent with the component namespace. metadata: namespace: Namespace spec: { // Ensure the target secret name is consistent. target: name: metadata.name // Ensure the name in the SecretStore is consistent. dataFrom: [{extract: {key: metadata.name}}] refreshInterval: "30s" secretStoreRef: kind: "SecretStore" secretStoreRef: name: "default" } } } ``` ```bash EOF ``` :::important Holos uses CUE to validate mixed in resources against a schema. The `Resources` field validates against the `#Resources` definition in [resources.cue]. ::: ### Importing CRDs Holos includes CUE schema definitions for the ExternalSecret Custom Resource Definition (CRD). These schemas are located in the `cue.mod` directory, generated by the `holos init platform` command executed at the start of this tutorial. To import your own custom resource definitions, use [Timoni]. We imported the ExternalSecret CRDs embedded in `holos` using the following command. ```bash timoni mod vendor crds -f https://raw.githubusercontent.com/external-secrets/external-secrets/v0.10.5/deploy/crds/bundle.yaml ``` ```txt 2:22PM INF schemas vendored: external-secrets.io/clusterexternalsecret/v1beta1 2:22PM INF schemas vendored: external-secrets.io/clustersecretstore/v1alpha1 2:22PM INF schemas vendored: external-secrets.io/clustersecretstore/v1beta1 2:22PM INF schemas vendored: external-secrets.io/externalsecret/v1alpha1 2:22PM INF schemas vendored: external-secrets.io/externalsecret/v1beta1 2:22PM INF schemas vendored: external-secrets.io/pushsecret/v1alpha1 2:22PM INF schemas vendored: external-secrets.io/secretstore/v1alpha1 2:22PM INF schemas vendored: external-secrets.io/secretstore/v1beta1 2:22PM INF schemas vendored: generators.external-secrets.io/acraccesstoken/v1alpha1 2:22PM INF schemas vendored: generators.external-secrets.io/ecrauthorizationtoken/v1alpha1 2:22PM INF schemas vendored: generators.external-secrets.io/fake/v1alpha1 2:22PM INF schemas vendored: generators.external-secrets.io/gcraccesstoken/v1alpha1 2:22PM INF schemas vendored: generators.external-secrets.io/githubaccesstoken/v1alpha1 2:22PM INF schemas vendored: generators.external-secrets.io/password/v1alpha1 2:22PM INF schemas vendored: generators.external-secrets.io/uuid/v1alpha1 2:22PM INF schemas vendored: generators.external-secrets.io/vaultdynamicsecret/v1alpha1 2:22PM INF schemas vendored: generators.external-secrets.io/webhook/v1alpha1 ``` :::tip Take a look at [cue.mod/gen/external-secrets.io/externalsecret/v1beta1/types_gen.cue] to see the imported definitions. ::: Once imported, the final step is to add the resource kind to the `#Resources` struct. Typically, this is done by creating a new file that CUE unifies with the existing [resources.cue] file. ## Reviewing Changes Render the platform with the `ExternalSecret` mixed into the podinfo component. ```shell holos render platform ``` Take a look at the diff to see the mixed in `ExternalSecret`. ```shell git diff deploy ``` ```diff diff --git a/deploy/components/podinfo/podinfo.gen.yaml b/deploy/components/podinfo/podinfo.gen.yaml index 6e4aec0..f79e9d0 100644 --- a/deploy/components/podinfo/podinfo.gen.yaml +++ b/deploy/components/podinfo/podinfo.gen.yaml @@ -112,3 +112,19 @@ spec: volumes: - emptyDir: {} name: data +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: podinfo + namespace: default +spec: + dataFrom: + - extract: + key: podinfo + refreshInterval: 30s + secretStoreRef: + kind: SecretStore + name: default + target: + name: podinfo ``` We saw how to mix in resources using the `Resources` field of the [ComponentConfig]. This approach works for every kind of component in Holos, allowing seamless integration without needing to fork the upstream Helm chart. This way, we can easily update to new podinfo versions as they're released. ## Trying Locally Optionally, apply the manifests rendered by Holos to a [Local Cluster] for testing. ## Next Steps This tutorial uses the `#Resources` structure to map resource kinds to their schema definitions in CUE. This structure is defined in `resources.cue` at the root of the tree. Take a look at [resources.cue] to see this mapping structure. [Local Cluster]: ../topics/local-cluster.mdx [ExternalSecret]: https://external-secrets.io/latest/api/externalsecret/ [Artifact]: ../api/core.md#Artifact [Resources]: ../api/core.md#Resources [Generator]: ../api/core.md#Generator [Hello Holos]: ./hello-holos.mdx [cue.mod/gen/external-secrets.io/externalsecret/v1beta1/types_gen.cue]: https://github.com/holos-run/holos/blob/main/internal/generate/platforms/cue.mod/gen/external-secrets.io/externalsecret/v1beta1/types_gen.cue#L13 [ComponentConfig]: ../api/author.md#ComponentConfig [timoni]: https://timoni.sh/install/ [resources.cue]: https://github.com/holos-run/holos/blob/main/internal/generate/platforms/v1alpha5/resources.cue#L33