Define and validate Kubernetes custom resources in modules.
Timoni allows defining Kubernetes Custom Resources (CRs) in modules and can ensure
that these are validated against their Kubernetes Custom Resource Definitions (CRDs).For waiting on custom resources that don’t follow the kstatus conventions,
see custom health checks.To enable validation for custom resources, you have to generate the CUE schemas from
the Kubernetes CRDs OpenAPI validation spec with the timoni mod vendor crds command.
From the root dir of your module, run the timoni mod vendor crds command, and pass the URL
to the YAML file which contains the Prometheus Operator CRDs:
timoni mod vendor crds -f https://github.com/prometheus-operator/prometheus-operator/releases/latest/download/stripped-down-crds.yaml
The above command will generate the CUE schemas corresponding to the Kubernetes CRDs
inside the cue.mod/gen directory:
CEL validation rulesThe generated files embed the original CRD in a hidden _crd field.
This lets timoni mod vet enforce the CEL rules set with x-kubernetes-validations,
such as mutually exclusive fields or constraints across field values,
applying the same strict validation as the Kubernetes API server.
By default, all the CRDs and all their versions found in the YAML file are vendored.
To vendor only the custom resources used by the module, select them with the
--kind and --version flags:
timoni mod vendor crds -f stripped-down-crds.yaml --kind ServiceMonitor,PodMonitor --version v1
The --kind flag accepts the kind name or the fully qualified Kind.group name. The qualified form selects a single CRD when the same
kind is defined in multiple API groups, e.g. --kind ProviderConfig.aws.upbound.io.
The --version flag applies to all the selected kinds and matches the version names
declared in the CRDs, e.g. --version v1,v1beta1. Both flags match case-insensitively.The command fails when a selected kind or version is not found in the YAML file,
listing the kinds and versions available.To list the kinds and versions found in a YAML file without vendoring them,
use the --list flag:
The command never removes the previously vendored schemas, so narrowing the selection
or vendoring a new release of the CRDs leaves the stale definitions in cue.mod/gen.
To remove them, use the --prune flag:
timoni mod vendor crds -f stripped-down-crds.yaml --kind ServiceMonitor --prune
Pruning is scoped to the API groups found in the YAML file: every vendored definition
under those groups that was not generated by the current run is removed, while the
definitions of other API groups, such as the ones vendored with timoni mod vendor k8s
or from another CRD file, are left untouched. Only the files generated by Timoni are removed,
hand-written CUE files placed under the same API group are kept.
In the templates directory, create a servicemonitor.cue file with the following content:
package templatesimport ( promv1 "monitoring.coreos.com/servicemonitor/v1")#ServiceMonitor: promv1.#ServiceMonitor & { #config: #Config metadata: #config.metadata spec: { endpoints: [{ // Change this to match the Service port where // your app exposes the /metrics endpoint port: "http-metrics" path: "/metrics" interval: "\(#config.monitoring.interval)s" }] namespaceSelector: matchNames: [#config.metadata.namespace] selector: matchLabels: #config.selector.labels }}
Make sure to replace the port and path values with the ones used by your app.
The port name must match one of the ports exposed in the Kubernetes Service template.
API Version and KindNote that for Kubernetes custom resources, you don’t need to specify the
apiVersion and kind, these fields are set by Timoni in the generated schema.
Finally, document the monitoring configuration in the README.md file, so that users
know how to enable monitoring if they have Prometheus Operator installed.
The timoni mod vet command validates the custom resources rendered by the module
against their CRD schemas with the same checks as the Kubernetes API server:
OpenAPI schema validation
embedded resource metadata validation
uniqueness of x-kubernetes-list-type: map and set items
the CUE packages imported by the module, generated with timoni mod vendor crds
the CRDs included in the module’s output
When both sources define the same kind and version, the CRD included in the module takes precedence.Custom resources that pass all checks are reported as valid custom resource.
Any violation is reported with the field path and the message, and the command fails:
$ timoni mod vet --debugvetting with debug valuesHTTPRoute/default/app spec.endpoints: spec.rules[0].filters[0]: filter.requestRedirect must be specified for RequestRedirect filter.typevalidation failed, 1 invalid custom resource(s)
The same checks run in timoni build, timoni bundle build and timoni bundle apply,
where only the violations are reported and --validate=false disables the validation.
In a bundle, the custom resources of an instance are also validated against the
CRDs rendered by the other instances, with bundle apply by the instances applied
before it, see bundle validation.
Was this page helpful?
{"docsConfig":{"theme":"aspen","$schema":"https://mintlify.com/docs.json","name":"Timoni","description":"Timoni is a package manager for Kubernetes powered by CUE lang.","colors":{"primary":"#7e56c2","light":"#a98be0","dark":"#6500b0"},"logo":{"light":"https://mintcdn.com/timoni/8xOqj2ulwazG1u2D/images/logo_text_black.svg?fit=max\u0026auto=format\u0026n=8xOqj2ulwazG1u2D\u0026q=85\u0026s=85ffbf62ec541a621a19255513199222","dark":"https://mintcdn.com/timoni/8xOqj2ulwazG1u2D/images/logo_text_white.svg?fit=max\u0026auto=format\u0026n=8xOqj2ulwazG1u2D\u0026q=85\u0026s=89788f7e520a7c81896a693804eb502d","href":"/"},"favicon":"https://mintcdn.com/timoni/8xOqj2ulwazG1u2D/images/favicon.png?fit=max\u0026auto=format\u0026n=8xOqj2ulwazG1u2D\u0026q=85\u0026s=214f9ea61f808abf055c6211a83d221c","appearance":{"default":"system"},"background":{"decoration":"gradient"},"navbar":{"links":[{"icon":"download","href":"/install","label":"Install"},{"icon":"github-alt","href":"https://github.com/stefanprodan/timoni","label":"GitHub"}]},"footer":{"socials":{"github":"https://github.com/stefanprodan/timoni","slack":"https://cloud-native.slack.com/team/ULPRMFH38","x":"https://x.com/stefanprodan","linkedin":"https://linkedin.com/in/stefanprodan"}},"seo":{"metatags":{"twitter:site":"@stefanprodan","twitter:creator":"@stefanprodan","twitter:card":"summary_large_image"},"indexing":"navigable"},"styling":{"eyebrows":"breadcrumbs","codeblocks":"system","latex":false},"errors":{"404":{"redirect":false,"title":"Page not found","description":"The page you are looking for does not exist or has moved.\n\n\u003cCardGroup cols={2}>\n \u003cCard title=\"Quickstart\" icon=\"rocket\" href=\"/quickstart\">Deploy your first module in minutes.\u003c/Card>\n \u003cCard title=\"Installation\" icon=\"download\" href=\"/install\">Get the Timoni CLI for your OS.\u003c/Card>\n \u003cCard title=\"CUE Reference\" icon=\"code\" href=\"/cue/module/initialization\">Author Timoni modules with CUE.\u003c/Card>\n \u003cCard title=\"CLI Reference\" icon=\"terminal\" href=\"/cmd/timoni\">Every timoni command and flag.\u003c/Card>\n\u003c/CardGroup>\n\nFollowed a broken link? [Open an issue](https://github.com/stefanprodan/timoni/issues) so we can fix it."}},"contextual":{"options":["copy","mcp","view","cursor","vscode"],"display":"header"},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"timoni","hasAudioNarration":false,"actualSubdomain":"timoni","customDomains":["timoni.sh"],"gitSource":{"type":"github","owner":"stefanprodan","repo":"timoni","deployBranch":"website","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"chroma":{"latestBuiltDeploymentHistoryId":"6ae489bca3eed560d7e508f5","collectionId":"c8a53a18-8419-4450-b32a-35cdecb6ffb2","liveDeploymentHistoryId":"6ae489bca3eed560d7e508f5","schemaVersion":"v3"},"feedback":{"thumbs":true},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6ae489bca3eed560d7e508f5","clientVersion":"0.0.4074","searchFilterCounts":[{"language":"default","tag":"CLI Reference","count":49},{"language":"default","tag":"Documentation","count":16},{"language":"default","tag":"CUE Reference","count":14},{"language":"default","tag":"Module","count":13},{"language":"default","tag":"Instance","count":9},{"language":"default","tag":"Module Development","count":9},{"language":"default","tag":"Artifact","count":7},{"language":"default","tag":"Bundle","count":7},{"language":"default","tag":"App Delivery","count":6},{"language":"default","tag":"Integrations","count":5}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true,"deflection":{"enabled":true,"email":"[email protected]"}},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":"body:has(#timoni-home) #navbar div:has(>.nav-tabs){display:none}#body-content,#navbar>div.mx-auto{max-width:100rem!important}@media (min-width: 1024px){#content-container{max-width:none!important;padding-left:3rem!important}}#header>.text-lg.prose{display:none}body:has(#timoni-home) #body-content{max-width:none!important}.tm-gradient{background:linear-gradient(135deg, #7e56c2 0%, #6500b0 60%, #2b1a4a 100%)}.tm-on-gradient{color:rgba(255, 255, 255, 0.8)}.tm-outline-light{color:#fff;border:1px solid rgba(255, 255, 255, 0.5)}.tm-outline-light:hover{border-color:#fff;background:rgba(255, 255, 255, 0.1)}.tm-band{background:#faf8fd;border-top:1px solid #ebe6f4;border-bottom:1px solid #ebe6f4}html.dark .tm-band{background:rgba(255, 255, 255, 0.025);border-color:rgba(255, 255, 255, 0.07)}.tm-check{display:inline-flex;flex:none;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin-top:0.1rem;border-radius:9999px;background:rgba(126, 86, 194, 0.12);color:#7e56c2}.tm-check svg{width:0.65rem!important;height:0.65rem!important}html.dark .tm-check{background:rgba(169, 139, 224, 0.18);color:#a98be0}.tm-diagram-frame{padding:1.25rem;border:1px solid #ebe6f4;border-radius:1rem;background:#fff}html.dark .tm-diagram-frame{border-color:rgba(255, 255, 255, 0.08);background:rgba(255, 255, 255, 0.02)}.tm-footer{border-top:1px solid #ebe6f4}html.dark .tm-footer{border-color:rgba(255, 255, 255, 0.07)}.tm-footer ul a{color:#4b5563}html.dark .tm-footer ul a{color:#9ca3af}.tm-footer ul a:hover{color:#7e56c2}html.dark .tm-footer ul a:hover{color:#a98be0}.tm-footer-bottom{border-top:1px solid #ebe6f4}html.dark .tm-footer-bottom{border-color:rgba(255, 255, 255, 0.07)}.tm-footer-socials{display:inline-flex;gap:1rem}.tm-footer-socials a{color:#6b7280}.tm-footer-socials a:hover{color:#7e56c2}.tm-footer-socials svg{width:1.1rem!important;height:1.1rem!important}#navbar .navbar-link>a{width:30px;height:30px;padding:0;justify-content:center;border-radius:9999px;background:rgba(31, 41, 55, 0.04)}html.dark #navbar .navbar-link>a{background:rgba(255, 255, 255, 0.1)}#navbar .navbar-link>a>span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap}#navbar .navbar-link>a>svg{width:1rem;height:1rem;background-color:#4b5563}#navbar .navbar-link>a:hover>svg{background-color:#1f2937}html.dark #navbar .navbar-link>a>svg{background-color:#d1d5db}html.dark #navbar .navbar-link>a:hover>svg{background-color:#f3f4f6}.not-found-container{max-width:44rem!important;padding-top:8rem!important}.not-found-description .card-group{width:100%;text-align:left}.not-found-description .card-group .card{text-align:left}#navbar img.nav-logo{height:30px}[data-component-part=\"card-icon\"] img[src*=\"/images/icons/\"]{object-fit:none;object-position:-9999px -9999px;background-color:rgb(var(--primary));mask-repeat:no-repeat;mask-position:center;mask-size:contain}html.dark [data-component-part=\"card-icon\"] img[src*=\"/images/icons/\"]{background-color:rgb(var(--primary-light))}[data-component-part=\"card-icon\"] img[src*=\"/images/icons/kubernetes.svg\"]{mask-image:url(\"/images/icons/kubernetes.svg\")}[data-component-part=\"card-icon\"] img[src*=\"/images/icons/helm.svg\"]{mask-image:url(\"/images/icons/helm.svg\")}[data-component-part=\"card-icon\"] img[src*=\"/images/icons/cue.svg\"]{mask-image:url(\"/images/icons/cue.svg\")}[data-component-part=\"card-icon\"] img[src*=\"/images/icons/flux.svg\"]{mask-image:url(\"/images/icons/flux.svg\")}img.tm-hero-logo{object-fit:none;object-position:-9999px -9999px;background-color:#111827;mask-image:url(\"/images/logo_icons.svg\");mask-repeat:no-repeat;mask-position:center;mask-size:contain}html.dark img.tm-hero-logo{background-color:#fff}"}],"page":{"pagePath":"cue/module/custom-resources","slug":"/cue/module/custom-resources","pageMetadata":{"title":"Kubernetes Custom Resources","sidebarTitle":"Add custom resources","description":"Define and validate Kubernetes custom resources in modules.","href":"/cue/module/custom-resources"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Define and validate Kubernetes custom resources in modules.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Tip}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Tip)_missingMdxReference(`Tip`,true);return _jsxs(_Fragment,{children:[_jsx(_components.p,{children:`Timoni allows defining Kubernetes Custom Resources (CRs) in modules and can ensure\nthat these are validated against their Kubernetes Custom Resource Definitions (CRDs).`}),`\n`,_jsxs(_components.p,{children:[`For waiting on custom resources that don’t follow the kstatus conventions,\nsee `,_jsx(_components.a,{href:`/cue/module/health-checks`,children:`custom health checks`}),`.`]}),`\n`,_jsxs(_components.p,{children:[`To enable validation for custom resources, you have to generate the CUE schemas from\nthe Kubernetes CRDs OpenAPI validation spec with the `,_jsx(_components.code,{children:`timoni mod vendor crds`}),` command.`]}),`\n`,_jsx(Heading,{level:`2`,id:`example`,children:`Example`}),`\n`,_jsxs(_components.p,{children:[`To demonstrate this feature, we’ll use the\n`,_jsx(_components.a,{href:`https://github.com/prometheus-operator/prometheus-operator`,children:`Prometheus Operator`}),` CRDs,\nand we’ll add a `,_jsx(_components.code,{children:`ServiceMonitor`}),` custom resource to a Timoni module.`]}),`\n`,_jsx(Heading,{level:`3`,id:`vendor-prometheus-operator-crds`,children:`Vendor Prometheus Operator CRDs`}),`\n`,_jsxs(_components.p,{children:[`From the root dir of your module, run the `,_jsx(_components.code,{children:`timoni mod vendor crds`}),` command, and pass the URL\nto the YAML file which contains the Prometheus Operator CRDs:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`timoni`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` mod`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` vendor`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` crds`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -f`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` https://github.com/prometheus-operator/prometheus-operator/releases/latest/download/stripped-down-crds.yaml`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`The above command will generate the CUE schemas corresponding to the Kubernetes CRDs\ninside the `,_jsx(_components.code,{children:`cue.mod/gen`}),` directory:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`13`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`13`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`cue.mod/gen/`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`└── monitoring.coreos.com`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ├── alertmanager`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ├── alertmanagerconfig`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ├── podmonitor`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ├── probe`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ├── prometheus`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ├── prometheusagent`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ├── prometheusrule`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ├── scrapeconfig`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ├── servicemonitor`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` └── thanosruler`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(Tip,{children:[_jsx(_components.p,{children:_jsx(_components.strong,{children:`CEL validation rules`})}),_jsxs(_components.p,{children:[`The generated files embed the original CRD in a hidden `,_jsx(_components.code,{children:`_crd`}),` field.\nThis lets `,_jsx(_components.code,{children:`timoni mod vet`}),` enforce the CEL rules set with `,_jsx(_components.code,{children:`x-kubernetes-validations`}),`,\nsuch as mutually exclusive fields or constraints across field values,\napplying the same strict validation as the Kubernetes API server.`]})]}),`\n`,_jsx(Heading,{level:`3`,id:`select-kinds-and-versions`,children:`Select kinds and versions`}),`\n`,_jsxs(_components.p,{children:[`By default, all the CRDs and all their versions found in the YAML file are vendored.\nTo vendor only the custom resources used by the module, select them with the\n`,_jsx(_components.code,{children:`--kind`}),` and `,_jsx(_components.code,{children:`--version`}),` flags:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`timoni`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` mod`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` vendor`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` crds`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -f`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` stripped-down-crds.yaml`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` --kind`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` ServiceMonitor,PodMonitor`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` --version`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` v1`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`The `,_jsx(_components.code,{children:`--kind`}),` flag accepts the kind name or the fully qualified `,_jsx(_components.code,{children:`Kind.group`}),` name. The qualified form selects a single CRD when the same\nkind is defined in multiple API groups, e.g. `,_jsx(_components.code,{children:`--kind ProviderConfig.aws.upbound.io`}),`.\nThe `,_jsx(_components.code,{children:`--version`}),` flag applies to all the selected kinds and matches the version names\ndeclared in the CRDs, e.g. `,_jsx(_components.code,{children:`--version v1,v1beta1`}),`. Both flags match case-insensitively.`]}),`\n`,_jsx(_components.p,{children:`The command fails when a selected kind or version is not found in the YAML file,\nlisting the kinds and versions available.`}),`\n`,_jsxs(_components.p,{children:[`To list the kinds and versions found in a YAML file without vendoring them,\nuse the `,_jsx(_components.code,{children:`--list`}),` flag:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`4`,language:`shellsession`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellsession`,children:_jsxs(_components.code,{language:`shellsession`,numberOfLines:`4`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`$ timoni mod vendor crds -f stripped-down-crds.yaml --list --kind ServiceMonitor,PodMonitor`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#D4D4D4`},children:`KIND VERSION`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#D4D4D4`},children:`PodMonitor.monitoring.coreos.com v1`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#D4D4D4`},children:`ServiceMonitor.monitoring.coreos.com v1`})}),`\n`]})})}),`\n`,_jsx(Heading,{level:`3`,id:`prune-vendored-schemas`,children:`Prune vendored schemas`}),`\n`,_jsxs(_components.p,{children:[`The command never removes the previously vendored schemas, so narrowing the selection\nor vendoring a new release of the CRDs leaves the stale definitions in `,_jsx(_components.code,{children:`cue.mod/gen`}),`.\nTo remove them, use the `,_jsx(_components.code,{children:`--prune`}),` flag:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#DCDCAA`},children:`timoni`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` mod`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` vendor`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` crds`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` -f`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` stripped-down-crds.yaml`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` --kind`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` ServiceMonitor`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:` --prune`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Pruning is scoped to the API groups found in the YAML file: every vendored definition\nunder those groups that was not generated by the current run is removed, while the\ndefinitions of other API groups, such as the ones vendored with `,_jsx(_components.code,{children:`timoni mod vendor k8s`}),`\nor from another CRD file, are left untouched. Only the files generated by Timoni are removed,\nhand-written CUE files placed under the same API group are kept.`]}),`\n`,_jsxs(Heading,{level:`3`,id:`create-the-servicemonitor-template`,children:[`Create the `,_jsx(_components.code,{children:`ServiceMonitor`}),` template`]}),`\n`,_jsxs(_components.p,{children:[`In the `,_jsx(_components.code,{children:`templates`}),` directory, create a `,_jsx(_components.code,{children:`servicemonitor.cue`}),` file with the following content:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`21`,language:`cue`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`cue`,children:_jsxs(_components.code,{language:`cue`,numberOfLines:`21`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`package`}),_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#4EC9B0`},children:` templates`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#569CD6`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` (`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#953800`,\"--shiki-dark\":`#4EC9B0`},children:`\tpromv1`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` \"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`monitoring.coreos.com/servicemonitor/v1`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`)`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#ServiceMonitor`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`promv1`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#ServiceMonitor`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` \u0026`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\t#config`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#Config`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\tmetadata`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#config`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`metadata`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\tspec`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\t\tendpoints`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: [{`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`\t\t\t// Change this to match the Service port where`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`\t\t\t// your app exposes the /metrics endpoint`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\t\t\tport`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`http-metrics`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\t\t\tpath`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`/metrics`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\t\t\tinterval`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\"`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\\\\(`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#config`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`monitoring`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`interval`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`)s`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\t\t}]`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\t\tnamespaceSelector`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`matchNames`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: [`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#config`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`metadata`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`namespace`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`]`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\t\tselector`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`matchLabels`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#config`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`selector`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`labels`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\t}`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Make sure to replace the `,_jsx(_components.code,{children:`port`}),` and `,_jsx(_components.code,{children:`path`}),` values with the ones used by your app.\nThe port name must match one of the ports exposed in the Kubernetes Service template.`]}),`\n`,_jsxs(Tip,{children:[_jsx(_components.p,{children:_jsx(_components.strong,{children:`API Version and Kind`})}),_jsxs(_components.p,{children:[`Note that for Kubernetes custom resources, you don’t need to specify the\n`,_jsx(_components.code,{children:`apiVersion`}),` and `,_jsx(_components.code,{children:`kind`}),`, these fields are set by Timoni in the generated schema.`]})]}),`\n`,_jsxs(Heading,{level:`3`,id:`add-the-monitoring-configuration`,children:[`Add the `,_jsx(_components.code,{children:`monitoring`}),` configuration`]}),`\n`,_jsxs(_components.p,{children:[`In the `,_jsx(_components.code,{children:`templates/config.cue`}),` file, add the `,_jsx(_components.code,{children:`monitoring`}),` configuration:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`10`,language:`cue`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`cue`,children:_jsxs(_components.code,{language:`cue`,numberOfLines:`10`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#Config`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`\t// Promethues service monitor (optional)`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\tmonitoring`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\t\tenabled`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`*`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:`false`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` |`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4EC9B0`},children:` bool`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\t\tinterval`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:`*`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`15`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` |`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#4EC9B0`},children:` int`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` \u0026`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` >=`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`5`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` \u0026`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` \u003c=`}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`3600`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\t}`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`]})})}),`\n`,_jsxs(Heading,{level:`3`,id:`add-the-servicemonitor-to-the-instance`,children:[`Add the `,_jsx(_components.code,{children:`ServiceMonitor`}),` to the instance`]}),`\n`,_jsxs(_components.p,{children:[`In the `,_jsx(_components.code,{children:`templates/config.cue`}),` file, add the `,_jsx(_components.code,{children:`ServiceMonitor`}),` resource to the instance objects:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`9`,language:`cue`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`cue`,children:_jsxs(_components.code,{language:`cue`,numberOfLines:`9`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#Instance`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\tconfig`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#Config`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`\tif`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:` config`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`monitoring`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`.`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`enabled`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`\t\tobjects`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`servicemonitor`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#ServiceMonitor`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#D4D4D4`},children:` \u0026`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` {`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`#config`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`config`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`\t}`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`]})})}),`\n`,_jsxs(Heading,{level:`3`,id:`document-the-monitoring-configuration`,children:[`Document the `,_jsx(_components.code,{children:`monitoring`}),` configuration`]}),`\n`,_jsxs(_components.p,{children:[`Finally, document the `,_jsx(_components.code,{children:`monitoring`}),` configuration in the `,_jsx(_components.code,{children:`README.md`}),` file, so that users\nknow how to enable monitoring if they have Prometheus Operator installed.`]}),`\n`,_jsx(Heading,{level:`2`,id:`custom-resource-validation`,children:`Custom resource validation`}),`\n`,_jsxs(_components.p,{children:[`The `,_jsx(_components.code,{children:`timoni mod vet`}),` command validates the custom resources rendered by the module\nagainst their CRD schemas with the same checks as the Kubernetes API server:`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`OpenAPI schema validation`}),`\n`,_jsx(_components.li,{children:`embedded resource metadata validation`}),`\n`,_jsxs(_components.li,{children:[`uniqueness of `,_jsx(_components.code,{children:`x-kubernetes-list-type: map`}),` and `,_jsx(_components.code,{children:`set`}),` items`]}),`\n`,_jsxs(_components.li,{children:[`CEL validation rules set by `,_jsx(_components.a,{href:`https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation-rules`,children:`x-kubernetes-validations`})]}),`\n`]}),`\n`,_jsx(_components.p,{children:`The CRD schemas are taken from:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`the CUE packages imported by the module, generated with `,_jsx(_components.code,{children:`timoni mod vendor crds`})]}),`\n`,_jsx(_components.li,{children:`the CRDs included in the module’s output`}),`\n`]}),`\n`,_jsx(_components.p,{children:`When both sources define the same kind and version, the CRD included in the module takes precedence.`}),`\n`,_jsxs(_components.p,{children:[`Custom resources that pass all checks are reported as `,_jsx(_components.code,{children:`valid custom resource`}),`.\nAny violation is reported with the field path and the message, and the command fails:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`4`,language:`shellsession`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellsession`,children:_jsxs(_components.code,{language:`shellsession`,numberOfLines:`4`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`$ timoni mod vet --debug`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#D4D4D4`},children:`vetting with debug values`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#D4D4D4`},children:`HTTPRoute/default/app spec.endpoints: spec.rules[0].filters[0]: filter.requestRedirect must be specified for RequestRedirect filter.type`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#D4D4D4`},children:`validation failed, 1 invalid custom resource(s)`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`The same checks run in `,_jsx(_components.code,{children:`timoni build`}),`, `,_jsx(_components.code,{children:`timoni bundle build`}),` and `,_jsx(_components.code,{children:`timoni bundle apply`}),`,\nwhere only the violations are reported and `,_jsx(_components.code,{children:`--validate=false`}),` disables the validation.\nIn a bundle, the custom resources of an instance are also validated against the\nCRDs rendered by the other instances, with `,_jsx(_components.code,{children:`bundle apply`}),` by the instances applied\nbefore it, see `,_jsx(_components.a,{href:`/bundle#validation`,children:`bundle validation`}),`.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"codeStyling":"system","config":{},"pageMetadata":{"title":"Kubernetes Custom Resources","sidebarTitle":"Add custom resources","description":"Define and validate Kubernetes custom resources in modules.","href":"/cue/module/custom-resources"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Example","slug":"example","depth":2,"children":[{"title":"Vendor Prometheus Operator CRDs","slug":"vendor-prometheus-operator-crds","depth":3,"children":[]},{"title":"Select kinds and versions","slug":"select-kinds-and-versions","depth":3,"children":[]},{"title":"Prune vendored schemas","slug":"prune-vendored-schemas","depth":3,"children":[]},{"title":"Create the ServiceMonitor template","slug":"create-the-servicemonitor-template","depth":3,"children":[]},{"title":"Add the monitoring configuration","slug":"add-the-monitoring-configuration","depth":3,"children":[]},{"title":"Add the ServiceMonitor to the instance","slug":"add-the-servicemonitor-to-the-instance","depth":3,"children":[]},{"title":"Document the monitoring configuration","slug":"document-the-monitoring-configuration","depth":3,"children":[]}]},{"title":"Custom resource validation","slug":"custom-resource-validation","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"timoni","siteName":"Timoni","title":"Kubernetes Custom Resources - Timoni","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-28T13:19:36.456Z","originalFileLocation":"cue/module/custom-resources.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Define and validate Kubernetes custom resources in modules.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Timoni\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/timoni/wBHd5XBsu-JLPEJB/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/timoni/wBHd5XBsu-JLPEJB/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/timoni/wBHd5XBsu-JLPEJB/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/timoni/wBHd5XBsu-JLPEJB/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/timoni/wBHd5XBsu-JLPEJB/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/timoni/wBHd5XBsu-JLPEJB/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/timoni/wBHd5XBsu-JLPEJB/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/timoni/wBHd5XBsu-JLPEJB/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/timoni/wBHd5XBsu-JLPEJB/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Kubernetes Custom Resources - Timoni\">\u003cmeta data-mint-head property=\"og:description\" content=\"Define and validate Kubernetes custom resources in modules.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Timoni\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://timoni.sh/cue/module/custom-resources\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://timoni.mintlify.site/_mintlify/api/og/v2?division=Module+Development\u0026amp;title=Kubernetes+Custom+Resources\u0026amp;description=Define+and+validate+Kubernetes+custom+resources+in+modules.\u0026amp;theme=d31bdd9b74b0932292c26bd2\u0026amp;signature=56c8d6fef3f38d31503c51e691e86418e5e377e4686d134fd1a428278fc96d47\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Kubernetes Custom Resources - Timoni\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Define and validate Kubernetes custom resources in modules.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://timoni.mintlify.site/_mintlify/api/og/v2?division=Module+Development\u0026amp;title=Kubernetes+Custom+Resources\u0026amp;description=Define+and+validate+Kubernetes+custom+resources+in+modules.\u0026amp;theme=d31bdd9b74b0932292c26bd2\u0026amp;signature=56c8d6fef3f38d31503c51e691e86418e5e377e4686d134fd1a428278fc96d47\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/timoni/wBHd5XBsu-JLPEJB/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Timoni\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#7e56c2\">\u003cmeta data-mint-head name=\"twitter:creator\" content=\"@stefanprodan\">\u003clink data-mint-head rel=\"canonical\" href=\"https://timoni.sh/cue/module/custom-resources\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/cue/module/custom-resources.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://timoni.sh/#organization\",\"name\":\"Timoni\",\"url\":\"https://timoni.sh\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/timoni/8xOqj2ulwazG1u2D/images/logo_text_black.svg?fit=max\u0026auto=format\u0026n=8xOqj2ulwazG1u2D\u0026q=85\u0026s=85ffbf62ec541a621a19255513199222\"}},{\"@type\":\"WebSite\",\"@id\":\"https://timoni.sh#website\",\"name\":\"Timoni\",\"url\":\"https://timoni.sh\",\"publisher\":{\"@id\":\"https://timoni.sh/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://timoni.sh/cue/module/custom-resources#webpage\",\"url\":\"https://timoni.sh/cue/module/custom-resources\",\"name\":\"Kubernetes Custom Resources\",\"description\":\"Define and validate Kubernetes custom resources in modules.\",\"dateModified\":\"2026-09-28T13:19:36.456Z\",\"isPartOf\":{\"@id\":\"https://timoni.sh#website\"},\"breadcrumb\":{\"@id\":\"https://timoni.sh/cue/module/custom-resources#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://timoni.sh/cue/module/custom-resources#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Module Development\",\"item\":\"https://timoni.sh/cue/module/initialization\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Kubernetes Custom Resources\",\"item\":\"https://timoni.sh/cue/module/custom-resources\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://timoni.sh/cue/module/custom-resources#article\",\"headline\":\"Kubernetes Custom Resources\",\"name\":\"Kubernetes Custom Resources\",\"description\":\"Define and validate Kubernetes custom resources in modules.\",\"url\":\"https://timoni.sh/cue/module/custom-resources\",\"mainEntityOfPage\":{\"@id\":\"https://timoni.sh/cue/module/custom-resources#webpage\"},\"dateModified\":\"2026-09-28T13:19:36.456Z\",\"publisher\":{\"@id\":\"https://timoni.sh/#organization\"},\"isPartOf\":{\"@id\":\"https://timoni.sh#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Timoni\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Timoni","nav":{"global":null,"tabs":[{"tab":"Documentation","icon":"book-open","pages":[{"title":"Documentation","href":"/quickstart"}]},{"tab":"CUE Reference","icon":"code","groups":[{"group":"CUE Language","pages":[{"title":"CUE Language","sidebarTitle":"Introduction","href":"/cue/introduction"},{"title":"CUE Features Walkthrough","sidebarTitle":"CUE Walkthrough","href":"/cue/walkthrough"}]},{"group":"Module Development","pages":[{"title":"Get Started with Timoni Modules","sidebarTitle":"Get started with modules","href":"/cue/module/initialization"},{"title":"Immutable ConfigMaps and Secrets","href":"/cue/module/immutable-config"},{"title":"Embedding files","description":"Embed configs, scripts and other plain files in modules with the @embed attribute.","href":"/cue/module/embedding-files"},{"title":"Kubernetes Custom Resources","sidebarTitle":"Add custom resources","href":"/cue/module/custom-resources"},{"title":"Kubernetes Version Constraints","sidebarTitle":"Cluster version constraints","description":"Adapt module output to the Kubernetes version of the target cluster.","href":"/cue/module/semver-constraints"},{"title":"Control the Apply Behavior","sidebarTitle":"Control the apply behavior","href":"/cue/module/apply-behavior"},{"title":"Custom Health Checks","sidebarTitle":"Custom health checks","href":"/cue/module/health-checks"},{"title":"Run tests with Kubernetes Jobs","href":"/cue/module/test-jobs"},{"title":"Import Kubernetes Resources from YAML","sidebarTitle":"Import resources from YAML","href":"/cue/module/import-resources"}]},{"group":"Module Distribution","pages":[{"title":"Module Publishing","sidebarTitle":"Publishing module versions","href":"/cue/module/publishing"},{"title":"Module Signing and Verification","sidebarTitle":"Signing and Verification","href":"/cue/module/signing"},{"title":"Module Distribution with GitHub Actions","sidebarTitle":"GitHub Actions","href":"/cue/module/github-actions"}]}]},{"tab":"CLI Reference","icon":"terminal","pages":[{"title":"CLI Reference","href":"/cmd/timoni"}]}]},"basePath":"","title":"Kubernetes Custom Resources - Timoni","buildId":"dpl_BLQvU86QFwq3AhPofSTw6q8dGWKm","deploymentId":"dpl_BLQvU86QFwq3AhPofSTw6q8dGWKm","dynamics":[],"chunks":[]}