Compare commits

..

77 Commits

Author SHA1 Message Date
Jeff McCune
13665fab55 core: define generic command core type for all kinds of tasks
Previously the Command core type was only useful for Validators and was
a bit hacky in that Holos appends the directory to the end of the
argument vector.

This patch changes the Command type to represent a generic command for
use as a Generator, Transformer, or Validator.  The type is extended to
support the semantics of reading output from a file, directory, or
standard output pipe.

Go templates are used to fill in Holos managed data values, for example
the temporary directory associated with the pipeline task.  The TaskData
structu represents these values passed into the template engine.
2025-03-03 17:07:56 -08:00
Jeff McCune
5a2571a745 docs: make generate for docs preview 2025-02-27 15:47:20 -08:00
Jeff McCune
bfd8b20b6a fixup: don't store a key with a trailing / in the artifact map 2025-02-27 15:29:27 -08:00
Jeff McCune
07cd8737b0 artifact: write multiple files if path ends in a /
To properly support the kubectl-slice use case the Artifact map needs to
write multiple files out to the filesystem.  This needs to be dynamic in
the sense holos and the end user don't know what files the kubectl-slice
transformer is producing.

As a hack, which may actually turn out to be "good enough" this patch
makes the Slice transformer behave like so:

1. Execute kubectl-slice outputting to an empty temp directory.
2. Holos saves all files in this directory into the artifact map.
3. At the end of the Artifact pipeline, if the final artifact produced
   ends in a /, then all keys in the artifact map having the prefix are
   written to the output directory.

This should be sufficient for the use case, but we'll need to consider
how this transformer and apporach works when subsequent transformers are
used in the pipeline.  I haven't thought deeply about it, but it should
ideally work pretty well if the tools involved truly only care about
directories and not the files within the directory.
2025-02-27 14:37:44 -08:00
Jeff McCune
ff5bdab948 transformer: wire up Slice transformer type
Copied from kustomize, spike for spiarh.

Result: head to artifact.go next.

could not run: could not save deploy/slice/components/slice: open deploy/slice/components/slice: is a directory at internal/artifact/artifact.go:71
could not run: could not render component: could not run command:
        holos '--log-level' 'debug' '--log-format' 'console' 'render' 'component' '--inject' 'outputBaseDir=slice' '--inject' 'holos_component_name=slice' '--inject' 'holos_component_path=components/slice' '--inject' 'holos_component_labels={"holos.run/component.name":"slice"}' '--inject' 'holos_component_annotations={"app.holos.run/description":"slice transformer"}' './components/slice'
        exit status 1 at cli/render/render.go:171

Relevant debug logs:

running command: kubectl 'kustomize' '/var/folders/22/zt67pphj6h1fgknqfy23ppl80000gn/T/holos.kustomize4273326823'
tmp: removed
running command: kubectl-slice '-f' '/var/folders/22/zt67pphj6h1fgknqfy23ppl80000gn/T/holos.slice2683550041/slice.gen.yaml' '-o' '/var/folders/22/zt67pphj6h1fgknqfy23ppl8000
0gn/T/holos.slice2683550041/slice'
storing: /var/folders/22/zt67pphj6h1fgknqfy23ppl80000gn/T/holos.slice2683550041/slice/deployment-httpbin.yaml
storing: /var/folders/22/zt67pphj6h1fgknqfy23ppl80000gn/T/holos.slice2683550041/slice/service-httpbin.yaml
tmp: removed
2025-02-27 12:56:01 -08:00
Jeff McCune
40093956d3 tools: add kubectl-slice 2025-02-27 09:22:13 -08:00
Jeff McCune
8e690b43ee ci: fix golangci-lint
Without this patch there are unexpected lint errors in version 1.60
where 1.61.0 passes locally on my machine.

This patch updates to:

    golangci-lint has version 1.64.5 built with go1.24.0 from 0a603e49 on 2025-02-13T21:19:55Z
2025-02-20 09:28:35 -08:00
Jeff McCune
a4ceb1cdb2 ci: update test cases with make bump
Previously using make bump to bump a version did not also update all of
the test cases and documentation to reflect the new version.  This patch
updates the make bump tasks call HOLOS_UPDATE_SCRIPTS=1 scripts/test to
keep the test cases and documentation in sync with the new version.
2025-02-20 09:14:37 -08:00
Jeff McCune
ddb5c0e07b ci: fix make lint failures resulting from version bumps
Without this patch lint fails with error:

    SA1019: testscript.RunMain is deprecated: use [Main]

This patch uses testscript.Main instead.
2025-02-20 09:07:47 -08:00
Jeff McCune
a14d3ba0f4 ci: fix make test failures resulting from version bumps
Previously the tests fail because they were not updated to use the new
version string in holos, or the new topo sort behavior in cue 0.12.0.

This patch updates the test cases using:

    HOLOS_UPDATE_SCRIPTS=1 scripts/test

Result: make test passes
2025-02-20 08:37:01 -08:00
Jeff McCune
f7e0470c48 version 0.104.0 with cue 0.12.0 2025-02-06 14:37:50 -08:00
Jeff McCune
d5c7b82684 go mod tidy 2025-02-06 14:33:14 -08:00
Jeff McCune
7d0392e596 update to cue 0.12.0
Most relevant for us: lots of fixes to the evaluator, enables the embed
and toposort experiments.
2025-02-06 14:31:39 -08:00
Gary Larizza
410b882d1d Merge pull request #403 from holos-run/gl/hello-holos-testscript
docs: Update Hello Holos tutorial to use testscript
2025-01-22 14:43:47 -08:00
Gary Larizza
e2648202dc Merge pull request #404 from holos-run/gl/kustomize-testscript
docs: Update Kustomize tutorial to use testscript
2025-01-22 14:43:33 -08:00
Jeff McCune
44c2fe220a test: fix helm capabilities test
Helm was upgraded in GitHub Actions resulting in an accidental failure
of the test case.
2025-01-17 12:33:28 -08:00
Jeff McCune
fe1ae2fa80 docs: migrate from an ApplicationSet blog post 2025-01-17 12:22:56 -08:00
Gary Larizza
8fbee1cbd9 docs: Update Kustomize tutorial to use testscript
PROBLEM:

The "Kustomize" tutorial has hardcoded code blocks and hasn't been
updated to use the automated testscript workflow.

SOLUTION:

Create a test for the Kustomize tutorial.
Create a testscript for the Kustomize test.
Update the Kustomize MDX file to load in data from the testscript directory.

OUTCOME:

The code content in the Kustomize tutorial now comes directly from the
testscript workflow.
2025-01-16 14:24:24 -08:00
Gary Larizza
982db2cccc docs: Update Hello Holos tutorial to use testscript
PROBLEM:

The "Hello Holos" tutorial has hardcoded code blocks and hasn't been
updated to use the automated testscript workflow.

SOLUTION:

* Create a test for the Hello Holos tutorial.
* Create a testscript for the Hello Holos test.
* Update the Hello Holos MDX file to load in data from the testscript directory.

OUTCOME:

The code content in the Hello Holos tutorial now comes directly from the
testscript workflow.
2025-01-16 10:12:17 -08:00
Jeff McCune
e9d1240d63 docs: make update-docs for version 0.103.0 2025-01-12 14:26:27 -08:00
Gary Larizza
03fa4eaaa2 docs: Helm Values test updates
* Convert all files with.period.separators to hyphen-separators.
* Rename and markdown_test.go to be specific to Helm Values.
* Move helm-values_test.go to be in the same directory as the Helm Values doc.
* Move Blackbox common configuration CUE file to `config/prometheus` so it can be imported as necessary.
* Use explicit import statements for Blackbox common config in `blackbox` and `prometheus` components.

Closes: #399
2025-01-12 14:25:44 -08:00
Jeff McCune
e363f3a597 docs: add make update-docs task
We need to run this prior to tagging a release otherwise the tests fail
for the new version string.
2025-01-12 14:22:58 -08:00
Jeff McCune
8b49ed93be docs: release version 0.103.0 2025-01-12 14:09:45 -08:00
Jeff McCune
d2be9fe278 helm: add valueFiles for migration from an ApplicationSet
Without this patch migrating from [helm hierarchies] to Holos requires
the user to unify the value hierarchy.  This is a problem because helm
hierarchies are difficult to unify because it's not clear if or why a
value is used in the final results.  This makes it difficult to identify
how to resolve conflicts.

This patch adds `valueFiles` field to the Helm component kind.  This
field is intended to provide a direct migration path from the
ApplicationSet.spec.template.spec.sources.helm.valueFiles field.  With
this patch, users can directly migrate the values files to CUE using
`@embed`, then directly migrate the valueFiles field to reference the
values from within CUE.

Note we actively discourage the use of Helm value hierarchies.  The
feature is intended as a temporary migration tool.  We encourage the use
of CUE unification instead.  After migration, the valueFiles field
should be refactored to the values field as one unified structure in
CUE.  The valueFiles field makes this second order migration easier
becuase we can inspect and verify the complete rendered output, allowing
us to determine if a value is actually used in the final configuration
or is overridden.

[helm hierarchies]: https://medium.com/containers-101/using-helm-hierarchies-in-multi-source-argo-cd-applications-for-promoting-to-different-gitops-133c3bc93678
2025-01-12 13:30:29 -08:00
Jeff McCune
6ec341bbb1 docs: redirect /docs/api/core 2025-01-10 15:02:12 -08:00
Jeff McCune
13a4305b78 docs: add redirect for /blog/rendered-manifest-pattern (manifest instead of manifests) 2025-01-10 14:50:26 -08:00
Jeff McCune
0cfce3a823 docs: redirect rendered manifests pattern for now
Need a URL we can redirect when we publish our own variation on the
pattern with a link back to Akuity.
2025-01-10 10:55:06 -08:00
Jeff McCune
61d7539e1c docs: fix /docs/guides/ redirect 2025-01-09 16:03:50 -08:00
Jeff McCune
bf84724137 docs: add redirects for github.com/holos-run readme 2025-01-09 15:11:04 -08:00
Jeff McCune
9f0de7555c init: change to holos.example default cue module
Match the cue mod init behavior of a module named `cue.example`.
2025-01-09 13:57:26 -08:00
Gary Larizza
650636f944 Merge pull request #393 from holos-run/gl/update-helm-docs
Update Helm Values Tutorial to use testscript
2025-01-09 12:01:09 -08:00
Gary Larizza
b28c110694 Update Helm Values tutorial to use testscript
PROBLEM:

The Helm Values tutorial contains a fair bit of code/scripts, and we
need a way to test the steps we recommend to make sure nothing breaks
or slips out of date.

SOLUTION:

* Use `testscript` as a way to automate the execution of the steps in the doc and verify that none of the steps produce errors.
* Update the MDX file to directly reference the files embedded into the testscript.

OUTCOME:

* We have an automated way to perform the steps in the Helm Values document.
* We have unit tests that will fail should any of the commands being executed in the doc fail.
* The doc's MDX file directly references the files within the testscript, so we only need to modify the MDX file to update wording.
2025-01-09 11:53:53 -08:00
Gary Larizza
5bb3e90b38 Install raw-loader module
We use this module within our markdown tutorials (like the Helm Values
tutorial) to load in files generated by testscript.
2025-01-09 11:53:13 -08:00
Jeff McCune
6a60b613ff render: fix selectors (#394)
Without this patch selectors don't work as expected.  This patch
fixes selectors such that each --selector flag value configures one
selector containing multiple positive or negative label matchers.

Result:

Render build plans for cluster dev or cluster test.  Note the use of two
flags indicating logical OR.

    holos render platform --selector cluster=test --selector cluster=dev
    rendered external-secrets for cluster test in 299.897542ms
    rendered external-secrets for cluster dev in 299.9225ms
    rendered external-secrets-crds for cluster test in 667.6075ms
    rendered external-secrets-crds for cluster dev in 708.126541ms
    rendered platform in 708.795625ms

Render build plans for prod clusters that are not customer facing.  Note
the use of one selector with comma separated labels.

    holos render platform --selector "tier=prod,scope!=customer"
2025-01-08 21:09:00 -08:00
Jeff McCune
5862725bab builder: deprecate ExtractYAML, use cue embed instead
Easier to place the data, better supported in the ecosystem.
2025-01-02 18:53:10 -08:00
Jeff McCune
8660826b05 builder: protect LoadInstance with a mutex
CUE is not safe for concurrent access so we protect the main
LoadInstance function with a mutex lock.
2025-01-02 17:32:53 -08:00
Jeff McCune
449df91e33 docs: app.holos.run/description not cli
The core component documentation on the annotation used to configure the
display line for each rendered component was incorrect.
2025-01-02 08:36:37 -08:00
Jeff McCune
ac59173b30 ci: update holos-run/holos-action version (try 3)
Fix the use of digests when pulling and pushing images.  Pull the image
from ghcr.io before pushing it to quay.io
2024-12-23 10:33:45 -08:00
Jeff McCune
fb75e560fc ci: update holos-run/holos-action version (try 2)
When new container image versions are built, automatically update the
holos-run/holos-action to use the new version.

Users of the action automatically update by default as a result.
2024-12-23 09:52:09 -08:00
Jeff McCune
69a064e3ea ci: update holos-run/holos-action version
When new container image versions are built, automatically update the
holos-run/holos-action to use the new version.

Users of the action automatically update by default as a result.
2024-12-23 07:23:36 -08:00
Jeff McCune
71b72807bb ci: tag v0.102.1 for container images
We need a released tag to reference in workflows that use the container
image to render the platform configuration.

This is the first image, subsequent git tags will also build and publish
container images.
2024-12-21 08:08:51 -08:00
Jeff McCune
0e4ecf9d13 ci: fix error in containers.yaml 2024-12-21 07:33:31 -08:00
Jeff McCune
ec2fdadd44 ci: build container from any ref
Too hard to try and build back in time, so let's just get it working
then build containers going forward for tags.
2024-12-21 07:31:09 -08:00
Jeff McCune
38b082095f ci: drop linux/arm/v7 support
There aren't kubectl images to build against.
2024-12-21 07:14:21 -08:00
Jeff McCune
f9346ea7c0 ci: use Dockerfile from main when building tags
Problem: We can't build old tags because the wrong Dockerfile is used
from the old tag.

Solution: Save the Dockerfile from main and use it to build the tag.
This create a dirty working directory but that's OK.
2024-12-21 07:11:29 -08:00
Jeff McCune
0f7010288a ci: build distroless container image for holos
Push it to ghcr and quay.

 * sign images with cosign and oidc id token
 * add kustomize v5.5.0 to tools for distroless image

Usage:

    docker run -v $(pwd):/app -w /app --rm -it ghcr.io/holos-run/holos:v0.101.8 holos render platform
2024-12-21 06:58:57 -08:00
Jeff McCune
386fb89cc6 ci: replace lint workflow with cspell
The lint workflow was slow and we don't often change buf or angular
these days so they're not necessary.

The remaining valuable task is cspell, which we can speed up with a
dedicated step.
2024-12-20 13:52:54 -08:00
Jeff McCune
c5401d6b02 ci: speed up tests by killing steps 2024-12-20 11:57:05 -08:00
Jeff McCune
f215405643 docs: fix links in readme 2024-12-20 07:28:04 -08:00
Jeff McCune
2c79982bd3 cue: enable @embed for loading yaml (#385)
mpvl suggests @embed is a more ideal solution than our implementation of
core.Component.Instances for the use case of unifying YAML data updated
by Kargo Stage resources.

See the issue for a link to the discussion.
2024-12-20 07:14:01 -08:00
Jeff McCune
e5e4de3073 cue: update to 0.11.1
go get cuelang.org/go/cmd/cue@latest

    go: downloading cuelang.org/go v0.11.1
    go: upgraded cuelang.org/go v0.11.0 => v0.11.1
2024-12-20 07:09:39 -08:00
Jeff McCune
ec462f5f0b docs: redirect /docs/support 2024-12-19 22:13:04 -08:00
Jeff McCune
0e95a2812e cmd: expose MakeMain() for testing
I'd like to add the kargo-demo repository to Unity to test evalv3, but
can't get a handle on the main function to wire up to testscript.

This patch fixes the problem by moving the MakeMain function to a public
package so the kargo-demo go module can import and call it using the go
mod tools technique.
2024-12-19 15:19:46 -08:00
Jeff McCune
54efe3e24a core: pass --extract-yaml flag from platform to component (#376)
Previously holos render platform was not setting the --extract-yaml file
when calling holos render component, causing data file instances defined
in the Platform spec to be discarded.

This patch passes the value along using the flag.
2024-12-19 08:39:55 -08:00
Jeff McCune
f693f049f4 core: refactor --instance to --extract-yaml (#376)
Extract YAML is more clear and aligns with the schema docs for the
Component Instance field which has an extractYAML kind.  This also
leaves the door open for additional kinds of data extractors which are
almost certainly going to be needed.
2024-12-19 08:34:05 -08:00
Jeff McCune
85238710ac core: unify data files into config (#376)
Previously there isn't a good way to unify json and yaml files with the
cue configuration.  This is a problem for use cases where data can be
generated idempotentialy prior to rendering the platform configuration.

The first use case is to explore unifying configuration with decrypted
sops values, which isn't typical since Holos is designed to handle
secrets with ExternalSecret resources, but does fit into the use case of
executing a command to produce data idempotently, then make the data
available to the platform configuration.

Other use cases this feature is intended to support are the prior
experiment where we fetch top level platform configuration from an rpc
service, and the future goal of integrating with data provided by
Terraform.
2024-12-19 08:34:05 -08:00
Jeff McCune
3ec62d272e v1alpha5: update kargo crds to 1.1.1 2024-12-19 08:34:04 -08:00
Jeff McCune
49afb44fd4 docs: redirect /docs/comparison/ 2024-12-18 14:37:36 -08:00
Gary Larizza
a023f135ab Add a Comparisons page
PROBLEM:

We've noticed that Holos almost immediately gets compared to Timoni, and
we frequently get asked for specifics in how they're similar/different.

SOLUTION:

* Add a `Comparison` page.
* Include a section that compares Holos to Timoni

OUTCOME:

Fewer questions about how Holos compares to Timoni because people are
able to find that answer themselves on our docs page.
2024-12-18 14:33:52 -08:00
Jeff McCune
c6a3a5d689 docs: redirect /docs/kargo/ 2024-12-17 06:30:20 -08:00
Jeff McCune
3f1eed3f06 platform: add kargo.akuity.io custom resource definitions
Needed for Kargo integration.  Imported with timoni from v1.0.3 Kargo
CRD's.
2024-12-16 13:19:39 -08:00
Jeff McCune
7fb7df1441 docs: make the linter happy 2024-12-16 11:04:35 -05:00
Jeff McCune
a798111d4d docs: add oci helm charts example
Question came up in chat, there isn't a good example and it's a pain to
piece together from the reference docs.
2024-12-16 10:56:50 -05:00
Jeff McCune
3ddb823341 docs: add note about compinit
Andy ran into issues enabling completion without first figuring out how
to initialize the completion system.
2024-12-16 08:15:45 -05:00
Jeff McCune
70d48592c4 docs: fix environments topic
It didn't work, failed with:

  ❯ holos show buildplans --selector app.holos.run/city=ams
  could not run: Component.Name: 2 errors in empty disjunction: (and 2 more errors) at internal/builder/instance.go:66
  Component.Name: 2 errors in empty disjunction:
  Component.Name: conflicting values "no-name" and "podinfo-ams":
      /Users/jeff/Holos/foo/holos-environments-tutorial/components/podinfo/podinfo.cue:6:12
      /Users/jeff/Holos/foo/holos-environments-tutorial/schema.cue:6:13
      /Users/jeff/Holos/foo/holos-environments-tutorial/schema.cue:35:2
      /Users/jeff/Holos/foo/holos-environments-tutorial/tags.cue:13:19
  Component.Name: conflicting values "podinfo" and "podinfo-ams":
      /Users/jeff/Holos/foo/holos-environments-tutorial/components/podinfo/podinfo.cue:6:12
      /Users/jeff/Holos/foo/holos-environments-tutorial/components/podinfo/podinfo.cue:7:8
      /Users/jeff/Holos/foo/holos-environments-tutorial/schema.cue:6:13
      /Users/jeff/Holos/foo/holos-environments-tutorial/schema.cue:35:2

This was likely because the podinfo component was used in different ways
in different topics.  Don't use the shared component to fix the problem.
2024-12-13 09:20:52 -05:00
Jeff McCune
006f08df93 docs: add kargo place holder (#378) 2024-12-11 09:58:54 -08:00
Jeff McCune
39e2db5d37 docs: remove related content from youtube embed
Except stuff in our own channel.
2024-12-08 19:43:12 -08:00
Jeff McCune
ceb293fd8a docs: fix typescript className not class check error 2024-12-08 19:36:36 -08:00
Jeff McCune
188ff95015 docs: enable youtube fullscreen
Without this patch the fullscreen button is disabled.
2024-12-08 19:33:06 -08:00
Jeff McCune
5f658e0ba0 docs: add flux kustomization example (#374)
Almost identical to the ArgoCD Application example.
2024-12-08 19:20:12 -08:00
Jeff McCune
18b2850d3c platform: import flux custom resources
kustomize build https://github.com/fluxcd/flux2/manifests/crds\?ref=v2.4.0 \
      timoni mod vendor crds -f-
2024-12-08 19:03:18 -08:00
Jeff McCune
366a7fe93d docs: private helm repos need updated schemas (#370)
Document the need to run holos init platform v1alpha5 --force to use the
private helm repository feature.
2024-12-08 17:13:56 -08:00
Jeff McCune
f71d6d5bd9 helm: support private helm repositories (#370)
Previously holos unconditionally executed helm repo add which failed for
private repositories requiring basic authentication.

This patch addresses the problem by using the Helm SDK to pull and cache
charts without adding them as repositories.  New fields for the
core.Helm type allow basic auth credentials to be read from environment
variables.

Multiple repositories are supported by using different env vars for
different repositories.
2024-12-06 15:38:46 -08:00
Gary Larizza
4529673e93 Embed YouTube Video (#369)
PROBLEM:

We've created a YouTube video walking people through Holos and the Helm
Values tutorial, but now we need to embed it on the site for visitors to
watch.

SOLUTION:

* Create a `YouTube` MDX plugin
* Use that plugin on Overview and Helm Values
* Tune the video size/attributes using CSS

OUTCOME:

The Helm Values YouTube video is embedded on our site for visitors to
watch.
2024-12-06 15:33:03 -08:00
Jeff McCune
16a6447926 helm: support oci images in chart name
Without this patch we do not support installing Kargo from an OCI helm
chart.  We want to support:

```
Component: #Helm & {
	Name:      "kargo"
	Namespace: Kargo.Namespace

	Chart: {
		name:    "oci://ghcr.io/akuity/kargo-charts/kargo"
		version: "1.0.3"
		release: Name
	}
	EnableHooks: true

	Values: Kargo.Values
}
```

This patch fixes the problem by using the base name for filesystem cache
operations.
2024-12-03 12:15:06 -08:00
Jeff McCune
111a5944ff cue: bump to 0.11.0
go get cuelang.org/go/cmd/cue@latest
2024-12-02 12:37:19 -08:00
Jeff McCune
ff1446dc93 docs: redirect /docs/guides/helm/
This shows up in the Unity tests I'm working on with mvdan and goes to a
blank page without the redirect in place.

	--- FAIL: TestGuides_v1alpha5 (0.00s)
	   --- FAIL: TestGuides_v1alpha5/helm (0.60s)
	       testscript.go:584: # Helm Guide https://holos.run/docs/guides/helm/
2024-12-02 09:05:13 -08:00
256 changed files with 14588 additions and 1280 deletions

View File

@@ -29,6 +29,7 @@
"authpolicy",
"authproxy",
"authroutes",
"autoload",
"automount",
"automounting",
"autoscaler",
@@ -36,6 +37,7 @@
"blackbox",
"buildplan",
"buildplans",
"Buildx",
"builtinpluginloadingoptions",
"cachedir",
"cadvisor",
@@ -44,6 +46,7 @@
"certificaterequest",
"certificaterequests",
"certificatesigningrequests",
"chartmuseum",
"clientset",
"clsx",
"clusterexternalsecret",
@@ -58,6 +61,7 @@
"Cmds",
"CNCF",
"CODEOWNERS",
"compinit",
"componentconfig",
"configdir",
"configmap",
@@ -71,6 +75,7 @@
"creds",
"crossplane",
"crunchydata",
"ctxt",
"cuecontext",
"cuelang",
"customresourcedefinition",
@@ -80,6 +85,7 @@
"destinationrules",
"devel",
"devicecode",
"distroless",
"dnsmasq",
"dscacheutil",
"ecrauthorizationtoken",
@@ -98,6 +104,7 @@
"fieldmaskpb",
"fieldspec",
"flushcache",
"fluxcd",
"fullname",
"gatewayclass",
"gatewayclasses",
@@ -151,6 +158,7 @@
"jetstack",
"jiralert",
"Jsonnet",
"Kargo",
"kfbh",
"killall",
"kubeadm",
@@ -188,6 +196,7 @@
"mutatingwebhookconfigurations",
"mvdan",
"mxcl",
"mychart",
"myhostname",
"myRegistrKeySecretName",
"mysecret",
@@ -274,6 +283,7 @@
"serviceentries",
"serviceentry",
"servicemonitor",
"sigstore",
"somevalue",
"SOMEVAR",
"sortoptions",
@@ -314,6 +324,7 @@
"udev",
"uibutton",
"Unmarshal",
"unshallow",
"unstage",
"untar",
"upbound",

143
.github/workflows/container.yaml vendored Normal file
View File

@@ -0,0 +1,143 @@
name: Container
# Only allow actors with write permission to the repository to trigger this
# workflow.
permissions:
contents: write
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
git_ref:
description: 'Git ref to build (e.g., refs/tags/v1.2.3, refs/heads/main)'
required: true
type: string
jobs:
buildx:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- name: Set tag from trigger event
id: opts
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "ref=${{ inputs.git_ref }}" >> $GITHUB_OUTPUT
else
echo "ref=${GITHUB_REF}" >> $GITHUB_OUTPUT
fi
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ steps.opts.outputs.ref }}
- name: SHA
id: sha
run: echo "sha=$(/usr/bin/git log -1 --format='%H')" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Fetch tags
run: git fetch --prune --unshallow --tags
- name: Set Tags
id: tags
run: |
echo "detail=$(/usr/bin/git describe --tags HEAD)" >> $GITHUB_OUTPUT
echo "suffix=$(test -n "$(git status --porcelain)" && echo '-dirty' || echo '')" >> $GITHUB_OUTPUT
echo "tag=$(/usr/bin/git describe --tags HEAD)$(test -n "$(git status --porcelain)" && echo '-dirty' || echo '')" >> $GITHUB_OUTPUT
- name: Login to ghcr.io
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push container images
id: build-and-push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
ghcr.io/holos-run/holos:${{ steps.tags.outputs.tag }}
ghcr.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}
- name: Setup Cosign to sign container images
uses: sigstore/cosign-installer@v3.7.0
- name: Sign with GitHub OIDC Token
env:
DIGEST: ${{ steps.build-and-push.outputs.digest }}
run: |
cosign sign --yes ghcr.io/holos-run/holos:${{ steps.tags.outputs.tag }}@${DIGEST}
cosign sign --yes ghcr.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}@${DIGEST}
- uses: actions/create-github-app-token@v1
id: app-token
with:
owner: ${{ github.repository_owner }}
app-id: ${{ vars.GORELEASER_APP_ID }}
private-key: ${{ secrets.GORELEASER_APP_PRIVATE_KEY }}
- name: Get GitHub App User ID
id: get-user-id
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
- run: |
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com'
- name: Update holos-run/holos-action
env:
IMAGE: ghcr.io/holos-run/holos:v0.102.1
VERSION: ${{ steps.tags.outputs.tag }}
USER_ID: ${{ steps.get-user-id.outputs.user-id }}
TOKEN: ${{ steps.app-token.outputs.token }}
run: |
set -euo pipefail
git clone "https://github.com/holos-run/holos-action"
cd holos-action
git remote set-url origin https://${USER_ID}:${TOKEN}@github.com/holos-run/holos-action
docker pull --quiet "${IMAGE}"
docker run -v $(pwd):/app --workdir /app --rm "${IMAGE}" \
holos cue export --out yaml action.cue -t "version=${VERSION}" > action.yml
git add action.yml
git commit -m "ci: update holos to ${VERSION} - https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" || (echo "No changes to commit"; exit 0)
git push origin HEAD:main HEAD:v0 HEAD:v1
- name: Login to quay.io
uses: docker/login-action@v3
with:
registry: quay.io
username: ${{ secrets.QUAY_USER }}
password: ${{ secrets.QUAY_TOKEN }}
- name: Push to quay.io
env:
DIGEST: ${{ steps.build-and-push.outputs.digest }}
run: |
# docker push quay.io/holos-run/holos:${{ steps.tags.outputs.tag }}
docker pull --quiet ghcr.io/holos-run/holos:${{ steps.tags.outputs.tag }}@${DIGEST}
docker tag ghcr.io/holos-run/holos:${{ steps.tags.outputs.tag }}@${DIGEST} \
quay.io/holos-run/holos:${{ steps.tags.outputs.tag }}
docker push quay.io/holos-run/holos:${{ steps.tags.outputs.tag }}
docker pull --quiet ghcr.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}@${DIGEST}
docker tag ghcr.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}@${DIGEST} \
quay.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}
docker push quay.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}
- name: Sign quay.io image
env:
DIGEST: ${{ steps.build-and-push.outputs.digest }}
run: |
cosign sign --yes quay.io/holos-run/holos:${{ steps.tags.outputs.tag }}@${DIGEST}
cosign sign --yes quay.io/holos-run/holos:${{ steps.sha.outputs.sha }}${{ steps.tags.outputs.suffix }}@${DIGEST}
outputs:
tag: ${{ steps.tags.outputs.tag }}
detail: ${{ steps.tags.outputs.detail }}

View File

@@ -27,4 +27,4 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.60
version: v1.64.5

View File

@@ -1,6 +1,5 @@
---
# https://github.com/golangci/golangci-lint-action?tab=readme-ov-file#how-to-use
name: Lint
name: Spelling
"on":
push:
branches:
@@ -8,35 +7,11 @@ name: Lint
- test
pull_request:
types: [opened, synchronize]
permissions:
contents: read
jobs:
lint:
name: lint
cspell:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: stable
## Not needed on ubuntu-latest
# - name: Install Packages
# run: sudo apt update && sudo apt -qq -y install git curl zip unzip tar bzip2 make
- name: Install Tools
run: make tools
- name: Lint
# golangci-lint runs in a separate workflow.
run: make lint -o golangci-lint
- uses: actions/checkout@v4
- run: ./hack/cspell

View File

@@ -28,19 +28,11 @@ jobs:
with:
go-version: stable
- name: Install Packages
run: sudo apt update && sudo apt -qq -y install git curl zip unzip tar bzip2 make
- name: Set up Helm
uses: azure/setup-helm@v4
- name: Set up Kubectl
uses: azure/setup-kubectl@v4
- name: Install Tools
run: |
set -x
make tools
- name: Test
run: ./scripts/test

1
.gitignore vendored
View File

@@ -12,3 +12,4 @@ tmp/
/holos-k3d/
/holos-infra/
node_modules/
.tmp/

View File

@@ -1,8 +1,31 @@
FROM quay.io/holos-run/debian:bullseye AS final
USER root
WORKDIR /app
ADD bin bin
RUN chown -R app: /app
# Kubernetes requires the user to be numeric
USER 8192
ENTRYPOINT bin/holos server
FROM registry.k8s.io/kubectl:v1.31.0 AS kubectl
# https://github.com/GoogleContainerTools/distroless
FROM golang:1.23 AS build
WORKDIR /go/src/app
COPY . .
RUN CGO_ENABLED=0 make install
RUN CGO_ENABLED=0 go install sigs.k8s.io/kustomize/kustomize/v5
# Install helm to /usr/local/bin/helm
# https://helm.sh/docs/intro/install/#from-script
# https://holos.run/docs/v1alpha5/tutorial/setup/#dependencies
RUN curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 \
&& chmod 700 get_helm.sh \
&& DESIRED_VERSION=v3.16.2 ./get_helm.sh \
&& rm -f get_helm.sh
COPY --from=kubectl /bin/kubectl /usr/local/bin/
# distroless
FROM gcr.io/distroless/static-debian12 AS final
COPY --from=build \
/go/bin/holos \
/go/bin/kustomize \
/usr/local/bin/kubectl \
/usr/local/bin/helm \
/bin/
# Usage: docker run -v $(pwd):/app --workdir /app --rm -it quay.io/holos-run/holos holos render platform
CMD ["/bin/holos"]

View File

@@ -32,17 +32,20 @@ bump: bumppatch
.PHONY: bumppatch
bumppatch: ## Bump the patch version.
scripts/bump patch
HOLOS_UPDATE_SCRIPTS=1 scripts/test
.PHONY: bumpminor
bumpminor: ## Bump the minor version.
scripts/bump minor
scripts/bump patch 0
HOLOS_UPDATE_SCRIPTS=1 scripts/test
.PHONY: bumpmajor
bumpmajor: ## Bump the major version.
scripts/bump major
scripts/bump minor 0
scripts/bump patch 0
HOLOS_UPDATE_SCRIPTS=1 scripts/test
.PHONY: show-version
show-version: ## Print the full version.
@@ -75,6 +78,12 @@ build: ## Build holos executable.
@echo "GOPATH=${GOPATH}"
go build -trimpath -o bin/$(BIN_NAME) -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/$(BIN_NAME)
.PHONY: debug
debug: ## Build debug executable.
@echo "building ${BIN_NAME}-debug ${VERSION}"
@echo "GOPATH=${GOPATH}"
go build -o bin/$(BIN_NAME)-debug $(REPO_PATH)/cmd/$(BIN_NAME)
linux: ## Build holos executable for tilt.
@echo "building ${BIN_NAME}.linux ${VERSION}"
@echo "GOPATH=${GOPATH}"
@@ -154,6 +163,10 @@ website: ## Build website
unity: ## https://cuelabs.dev/unity/
./scripts/unity
.PHONY: update-docs
update-docs: ## Update doc examples
HOLOS_UPDATE_SCRIPTS=1 go test -v ./doc/md/...
.PHONY: help
help: ## Display this help menu.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

View File

@@ -119,12 +119,12 @@ here to help.
Holos is licensed under Apache 2.0 as found in the [LICENSE file](LICENSE).
[Holos]: https://holos.run
[Holos]: https://holos.run/docs/overview/
[rendered manifests pattern]: https://akuity.io/blog/the-rendered-manifests-pattern
[CUE]: https://cuelang.org/
[Discord]: https://discord.gg/JgDVbNpye7
[GitHub discussions]: https://github.com/holos-run/holos/discussions
[Why CUE for Configuration]: https://holos.run/blog/why-cue-for-configuration/
[topics]: https://holos.run/docs/topics/
[tutorial]: https://holos.run/docs/overview/
[setup]: https://holos.run/docs/setup/
[tutorial]: https://holos.run/docs/tutorial/
[topics]: https://holos.run/docs/topics/

View File

@@ -84,6 +84,9 @@ type Helm struct {
Chart core.Chart
// Values represents data to marshal into a values.yaml for helm.
Values core.Values
// ValueFiles represents value files for migration from helm value
// hierarchies. Use Values instead.
ValueFiles []core.ValueFile `json:",omitempty"`
// EnableHooks enables helm hooks when executing the `helm template` command.
EnableHooks bool `cue:"true | *false"`
// Namespace sets the helm chart namespace flag if provided.

View File

@@ -118,8 +118,12 @@ type Helm struct {
// Chart represents a helm chart to manage.
Chart Chart `json:"chart" yaml:"chart"`
// Values represents values for holos to marshal into values.yaml when
// rendering the chart.
// rendering the chart. Values follow ValueFiles when both are provided.
Values Values `json:"values" yaml:"values"`
// ValueFiles represents hierarchial value files passed in order to the helm
// template -f flag. Useful for migration from an ApplicationSet. Use Values
// instead. ValueFiles precede Values when both are provided.
ValueFiles []ValueFile `json:"valueFiles,omitempty" yaml:"valueFiles,omitempty"`
// EnableHooks enables helm hooks when executing the `helm template` command.
EnableHooks bool `json:"enableHooks,omitempty" yaml:"enableHooks,omitempty"`
// Namespace represents the helm namespace flag
@@ -130,6 +134,17 @@ type Helm struct {
KubeVersion string `json:"kubeVersion,omitempty" yaml:"kubeVersion,omitempty"`
}
// ValueFile represents one Helm value file produced from CUE.
type ValueFile struct {
// Name represents the file name, e.g. "region-values.yaml"
Name string `json:"name" yaml:"name"`
// Kind is a discriminator.
Kind string `json:"kind" yaml:"kind" cue:"\"Values\""`
// Values represents values for holos to marshal into the file name specified
// by Name when rendering the chart.
Values Values `json:"values,omitempty" yaml:"values,omitempty"`
}
// Values represents [Helm] Chart values generated from CUE.
type Values map[string]any
@@ -146,9 +161,26 @@ type Chart struct {
}
// Repository represents a [Helm] [Chart] repository.
//
// The Auth field is useful to configure http basic authentication to the Helm
// repository. Holos gets the username and password from the environment
// variables represented by the Auth field.
type Repository struct {
Name string `json:"name" yaml:"name"`
URL string `json:"url" yaml:"url"`
Auth Auth `json:"auth,omitempty" yaml:"auth,omitempty"`
}
// Auth represents environment variable names containing auth credentials.
type Auth struct {
Username AuthSource `json:"username" yaml:"username"`
Password AuthSource `json:"password" yaml:"password"`
}
// AuthSource represents a source for the value of an [Auth] field.
type AuthSource struct {
Value string `json:"value,omitempty" yaml:"value,omitempty"`
FromEnv string `json:"fromEnv,omitempty" yaml:"fromEnv,omitempty"`
}
// Transformer combines multiple inputs from prior [Generator] or [Transformer]
@@ -158,11 +190,13 @@ type Repository struct {
// 1. [Kustomize] - Patch and transform the output from prior generators or
// transformers. See [Introduction to Kustomize].
// 2. [Join] - Concatenate multiple prior outputs into one output.
// 3. [Slice] - Slice an artifact into multiple artifacts using [kubectl-slice].
//
// [Introduction to Kustomize]: https://kubectl.docs.kubernetes.io/guides/config_management/introduction/
// [kubectl-slice]: https://github.com/patrickdappollonio/kubectl-slice
type Transformer struct {
// Kind represents the kind of transformer. Must be Kustomize, or Join.
Kind string `json:"kind" yaml:"kind" cue:"\"Kustomize\" | \"Join\""`
Kind string `json:"kind" yaml:"kind" cue:"\"Kustomize\" | \"Join\" | \"Slice\""`
// Inputs represents the files to transform. The Output of prior Generators
// and Transformers.
Inputs []FilePath `json:"inputs" yaml:"inputs"`
@@ -222,12 +256,46 @@ type Validator struct {
Command Command `json:"command,omitempty" yaml:"command,omitempty"`
}
// Command represents a command vetting one or more artifacts. Holos appends
// fully qualified input file paths to the end of the args list, then executes
// the command. Inputs are written into a temporary directory prior to
// executing the command and removed afterwards.
// Command represents a generic command for use as a Generator, Transformer, or
// Validator. Holos uses the Go template engine to render the Args field using
// data provided by the TaskData field. For example to fill in the fully
// qualified temporary directory used to provide inputs to the task.
type Command struct {
// DisplayName represents a friendly display name for the command.
DisplayName string `json:"displayName,omitempty" yaml:"displayName,omitempty"`
// Args represents the complete command argument vector as a go template.
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
// OutputRef references the source of the output data.
OutputRef OutputRef `json:"outputRef,omitempty" yaml:"outputRef,omitempty"`
// TaskData populated by Holos for template rendering.
TaskData TaskData `json:"taskData,omitempty" yaml:"taskData,omitempty"`
// TODO(jjm): add command environment variable support similar to args.
}
// TaskData represents data values associated with a pipeline task necessary to
// execute the task. For example, the randomly generated temporary directory
// used to read and write artifact files when executing user defined task
// commands. Values of this struct are intended for the Go template engine.
//
// Holos populates this struct as needed. Holos may treat user provided values
// as an error condition.
type TaskData struct {
// TempDir represents the temp directory holos manages for task artifacts.
TempDir string `json:"tempDir,omitempty" yaml:"tempDir,omitempty"`
}
// OutputRef represents a reference to the data source used as the output of a
// task. For example, a Generator output may be sourced from standard output or
// a file path.
type OutputRef struct {
// Kind represents the kind of output produced.
Kind string `json:"kind,omitempty" yaml:"kind,omitempty" cue:"string | *\"Pipe\" | \"Path\""`
// Pipe represents stdout or stderr. Ignored unless kind is Pipe.
Pipe string `json:"pipe,omitempty" yaml:"pipe,omitempty" cue:"string | *\"stdout\" | \"stderr\""`
// Path represents an artifact path relative to the task temp directory.
// Ignored unless kind is Path.
Path string `json:"path,omitempty" yaml:"path,omitempty"`
// TODO(jjm): support jsonpath or cel references to the output data maybe?
}
// InternalLabel is an arbitrary unique identifier internal to holos itself.
@@ -246,7 +314,7 @@ type Metadata struct {
// Labels represents a resource selector.
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
// Annotations represents arbitrary non-identifying metadata. For example
// holos uses the `cli.holos.run/description` annotation to log resources in a
// holos uses the `app.holos.run/description` annotation to log resources in a
// user customized way.
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
}
@@ -286,6 +354,10 @@ type Component struct {
// Path represents the path of the component relative to the platform root.
// Injected as the tag variable "holos_component_path".
Path string `json:"path" yaml:"path"`
// Instances represents additional cue instance paths to unify with Path.
// Useful to unify data files into a component BuildPlan. Added in holos
// 0.101.7.
Instances []Instance `json:"instances,omitempty" yaml:"instances,omitempty"`
// WriteTo represents the holos render component --write-to flag. If empty,
// the default value for the --write-to flag is used.
WriteTo string `json:"writeTo,omitempty" yaml:"writeTo,omitempty"`
@@ -299,6 +371,30 @@ type Component struct {
// resulting BuildPlan.
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
// Annotations represents arbitrary non-identifying metadata. Use the
// `cli.holos.run/description` to customize the log message of each BuildPlan.
// `app.holos.run/description` to customize the log message of each BuildPlan.
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
}
// Instance represents a data instance to unify with the configuration.
//
// Useful to unify json and yaml files with cue configuration files for
// integration with other tools. For example, executing holos render platform
// from a pull request workflow after [Kargo] executes the [yaml update] and
// [git wait for pr] promotion steps.
//
// [Kargo]: https://docs.kargo.io/
// [yaml update]: https://docs.kargo.io/references/promotion-steps#yaml-update
// [git wait for pr]: https://docs.kargo.io/references/promotion-steps#git-wait-for-pr
type Instance struct {
// Kind is a discriminator.
Kind string `json:"kind" yaml:"kind" cue:"\"ExtractYAML\""`
// Ignored unless kind is ExtractYAML.
ExtractYAML ExtractYAML `json:"extractYAML,omitempty" yaml:"extractYAML,omitempty"`
}
// ExtractYAML represents a cue data instance encoded as yaml or json. If Path
// refers to a directory all files in the directory are extracted
// non-recursively. Otherwise, path must refer to a file.
type ExtractYAML struct {
Path string `json:"path" yaml:"path"`
}

63
cmd/cmd.go Normal file
View File

@@ -0,0 +1,63 @@
package cmd
import (
"context"
"fmt"
"log/slog"
"os"
"runtime/pprof"
"runtime/trace"
"github.com/holos-run/holos/internal/cli"
"github.com/holos-run/holos/internal/holos"
)
// MakeMain makes a main function for the cli or tests.
func MakeMain(options ...holos.Option) func() int {
return func() (exitCode int) {
cfg := holos.New(options...)
slog.SetDefault(cfg.Logger())
ctx := context.Background()
if format := os.Getenv("HOLOS_CPU_PROFILE"); format != "" {
f, _ := os.Create(fmt.Sprintf(format, os.Getppid(), os.Getpid()))
err := pprof.StartCPUProfile(f)
defer func() {
pprof.StopCPUProfile()
f.Close()
}()
if err != nil {
return cli.HandleError(ctx, err, cfg)
}
}
defer memProfile(ctx, cfg)
if format := os.Getenv("HOLOS_TRACE"); format != "" {
f, _ := os.Create(fmt.Sprintf(format, os.Getppid(), os.Getpid()))
err := trace.Start(f)
defer func() {
trace.Stop()
f.Close()
}()
if err != nil {
return cli.HandleError(ctx, err, cfg)
}
}
feature := &holos.EnvFlagger{}
if err := cli.New(cfg, feature).ExecuteContext(ctx); err != nil {
return cli.HandleError(ctx, err, cfg)
}
return 0
}
}
func memProfile(ctx context.Context, cfg *holos.Config) {
if format := os.Getenv("HOLOS_MEM_PROFILE"); format != "" {
f, _ := os.Create(fmt.Sprintf(format, os.Getppid(), os.Getpid()))
defer f.Close()
if err := pprof.WriteHeapProfile(f); err != nil {
_ = cli.HandleError(ctx, err, cfg)
}
}
}

View File

@@ -3,9 +3,9 @@ package main
import (
"os"
"github.com/holos-run/holos/internal/cli"
"github.com/holos-run/holos/cmd"
)
func main() {
os.Exit(cli.MakeMain()())
os.Exit(cmd.MakeMain()())
}

View File

@@ -6,15 +6,16 @@ import (
"testing"
cue "cuelang.org/go/cmd/cue/cmd"
"github.com/holos-run/holos/internal/cli"
"github.com/holos-run/holos/cmd"
"github.com/rogpeppe/go-internal/testscript"
)
func TestMain(m *testing.M) {
os.Exit(testscript.RunMain(m, map[string]func() int{
"holos": cli.MakeMain(),
"cue": cue.Main,
}))
holosMain := cmd.MakeMain()
testscript.Main(m, map[string]func(){
"holos": func() { os.Exit(holosMain()) },
"cue": func() { os.Exit(cue.Main()) },
})
}
func TestGuides_v1alpha5(t *testing.T) {

View File

@@ -1,38 +0,0 @@
# https://github.com/holos-run/holos/issues/332
env HOME=$WORK
# Mock with a stub helm command
env PATH=$WORK/bin:$PATH
chmod 755 bin/helm
# Initialize the platform
exec holos init platform v1alpha5 --force
# when helm update returns an error
! exec holos render platform
# holos should log the helm error to stderr
stderr 'Error: chart "podinfo" matching 0.0.0 not found in podinfo index'
-- bin/helm --
#! /bin/bash
echo 'Error: chart "podinfo" matching 0.0.0 not found in podinfo index' >&2
exit 2
-- platform/podinfo.cue --
package holos
Platform: Components: podinfo: {
name: "podinfo"
path: "components/podinfo"
}
-- components/podinfo/podinfo.cue --
package holos
// Produce a helm chart build plan.
holos: HelmChart.BuildPlan
HelmChart: #Helm & {
Name: "podinfo"
Chart: {
version: "0.0.0"
repository: {
name: "podinfo"
url: "https://stefanprodan.github.io/podinfo"
}
}
}

View File

@@ -1,7 +1,9 @@
# https://github.com/holos-run/holos/issues/330
exec holos init platform v1alpha5 --force
# Make sure the helm chart works with plain helm
exec helm template ./components/capabilities/vendor/0.1.0/capabilities
cmp stdout want/helm-template.yaml
stdout 'name: has-foo-v1beta1'
stdout 'kubeVersion: v'
exec holos render platform ./platform
# When no capabilities are specified
cmp deploy/components/capabilities/capabilities.gen.yaml want/when-no-capabilities-specified.yaml

View File

@@ -31,7 +31,6 @@ spec:
- kind: Resources
output: resources.gen.yaml
resources: {}
validators: []
transformers:
- kind: Kustomize
inputs:
@@ -39,7 +38,8 @@ spec:
output: components/no-name/no-name.gen.yaml
kustomize:
kustomization:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- resources.gen.yaml
kind: Kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
validators: []

View File

@@ -86,6 +86,9 @@ type Helm struct {
Chart core.Chart
// Values represents data to marshal into a values.yaml for helm.
Values core.Values
// ValueFiles represents value files for migration from helm value
// hierarchies. Use Values instead.
ValueFiles []core.ValueFile `json:",omitempty"`
// EnableHooks enables helm hooks when executing the `helm template` command.
EnableHooks bool `cue:"true | *false"`
// Namespace sets the helm chart namespace flag if provided.

View File

@@ -15,30 +15,37 @@ Package core contains schemas for a [Platform](<#Platform>) and [BuildPlan](<#Bu
## Index
- [type Artifact](<#Artifact>)
- [type Auth](<#Auth>)
- [type AuthSource](<#AuthSource>)
- [type BuildPlan](<#BuildPlan>)
- [type BuildPlanSpec](<#BuildPlanSpec>)
- [type Chart](<#Chart>)
- [type Command](<#Command>)
- [type Component](<#Component>)
- [type ExtractYAML](<#ExtractYAML>)
- [type File](<#File>)
- [type FileContent](<#FileContent>)
- [type FileContentMap](<#FileContentMap>)
- [type FilePath](<#FilePath>)
- [type Generator](<#Generator>)
- [type Helm](<#Helm>)
- [type Instance](<#Instance>)
- [type InternalLabel](<#InternalLabel>)
- [type Join](<#Join>)
- [type Kind](<#Kind>)
- [type Kustomization](<#Kustomization>)
- [type Kustomize](<#Kustomize>)
- [type Metadata](<#Metadata>)
- [type OutputRef](<#OutputRef>)
- [type Platform](<#Platform>)
- [type PlatformSpec](<#PlatformSpec>)
- [type Repository](<#Repository>)
- [type Resource](<#Resource>)
- [type Resources](<#Resources>)
- [type TaskData](<#TaskData>)
- [type Transformer](<#Transformer>)
- [type Validator](<#Validator>)
- [type ValueFile](<#ValueFile>)
- [type Values](<#Values>)
@@ -65,6 +72,30 @@ type Artifact struct {
}
```
<a name="Auth"></a>
## type Auth {#Auth}
Auth represents environment variable names containing auth credentials.
```go
type Auth struct {
Username AuthSource `json:"username" yaml:"username"`
Password AuthSource `json:"password" yaml:"password"`
}
```
<a name="AuthSource"></a>
## type AuthSource {#AuthSource}
AuthSource represents a source for the value of an [Auth](<#Auth>) field.
```go
type AuthSource struct {
Value string `json:"value,omitempty" yaml:"value,omitempty"`
FromEnv string `json:"fromEnv,omitempty" yaml:"fromEnv,omitempty"`
}
```
<a name="BuildPlan"></a>
## type BuildPlan {#BuildPlan}
@@ -122,11 +153,18 @@ type Chart struct {
<a name="Command"></a>
## type Command {#Command}
Command represents a command vetting one or more artifacts. Holos appends fully qualified input file paths to the end of the args list, then executes the command. Inputs are written into a temporary directory prior to executing the command and removed afterwards.
Command represents a generic command for use as a Generator, Transformer, or Validator. Holos uses the Go template engine to render the Args field using data provided by the TaskData field. For example to fill in the fully qualified temporary directory used to provide inputs to the task.
```go
type Command struct {
// DisplayName represents a friendly display name for the command.
DisplayName string `json:"displayName,omitempty" yaml:"displayName,omitempty"`
// Args represents the complete command argument vector as a go template.
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
// OutputRef references the source of the output data.
OutputRef OutputRef `json:"outputRef,omitempty" yaml:"outputRef,omitempty"`
// TaskData populated by Holos for template rendering.
TaskData TaskData `json:"taskData,omitempty" yaml:"taskData,omitempty"`
}
```
@@ -143,6 +181,10 @@ type Component struct {
// Path represents the path of the component relative to the platform root.
// Injected as the tag variable "holos_component_path".
Path string `json:"path" yaml:"path"`
// Instances represents additional cue instance paths to unify with Path.
// Useful to unify data files into a component BuildPlan. Added in holos
// 0.101.7.
Instances []Instance `json:"instances,omitempty" yaml:"instances,omitempty"`
// WriteTo represents the holos render component --write-to flag. If empty,
// the default value for the --write-to flag is used.
WriteTo string `json:"writeTo,omitempty" yaml:"writeTo,omitempty"`
@@ -156,11 +198,22 @@ type Component struct {
// resulting BuildPlan.
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
// Annotations represents arbitrary non-identifying metadata. Use the
// `cli.holos.run/description` to customize the log message of each BuildPlan.
// `app.holos.run/description` to customize the log message of each BuildPlan.
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
}
```
<a name="ExtractYAML"></a>
## type ExtractYAML {#ExtractYAML}
ExtractYAML represents a cue data instance encoded as yaml or json. If Path refers to a directory all files in the directory are extracted non\-recursively. Otherwise, path must refer to a file.
```go
type ExtractYAML struct {
Path string `json:"path" yaml:"path"`
}
```
<a name="File"></a>
## type File {#File}
@@ -240,8 +293,12 @@ type Helm struct {
// Chart represents a helm chart to manage.
Chart Chart `json:"chart" yaml:"chart"`
// Values represents values for holos to marshal into values.yaml when
// rendering the chart.
// rendering the chart. Values follow ValueFiles when both are provided.
Values Values `json:"values" yaml:"values"`
// ValueFiles represents hierarchial value files passed in order to the helm
// template -f flag. Useful for migration from an ApplicationSet. Use Values
// instead. ValueFiles precede Values when both are provided.
ValueFiles []ValueFile `json:"valueFiles,omitempty" yaml:"valueFiles,omitempty"`
// EnableHooks enables helm hooks when executing the `helm template` command.
EnableHooks bool `json:"enableHooks,omitempty" yaml:"enableHooks,omitempty"`
// Namespace represents the helm namespace flag
@@ -253,6 +310,22 @@ type Helm struct {
}
```
<a name="Instance"></a>
## type Instance {#Instance}
Instance represents a data instance to unify with the configuration.
Useful to unify json and yaml files with cue configuration files for integration with other tools. For example, executing holos render platform from a pull request workflow after [Kargo](<https://docs.kargo.io/>) executes the [yaml update](<https://docs.kargo.io/references/promotion-steps#yaml-update>) and [git wait for pr](<https://docs.kargo.io/references/promotion-steps#git-wait-for-pr>) promotion steps.
```go
type Instance struct {
// Kind is a discriminator.
Kind string `json:"kind" yaml:"kind" cue:"\"ExtractYAML\""`
// Ignored unless kind is ExtractYAML.
ExtractYAML ExtractYAML `json:"extractYAML,omitempty" yaml:"extractYAML,omitempty"`
}
```
<a name="InternalLabel"></a>
## type InternalLabel {#InternalLabel}
@@ -317,12 +390,29 @@ type Metadata struct {
// Labels represents a resource selector.
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
// Annotations represents arbitrary non-identifying metadata. For example
// holos uses the `cli.holos.run/description` annotation to log resources in a
// holos uses the `app.holos.run/description` annotation to log resources in a
// user customized way.
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
}
```
<a name="OutputRef"></a>
## type OutputRef {#OutputRef}
OutputRef represents a reference to the data source used as the output of a task. For example, a Generator output may be sourced from standard output or a file path.
```go
type OutputRef struct {
// Kind represents the kind of output produced.
Kind string `json:"kind,omitempty" yaml:"kind,omitempty" cue:"string | *\"Pipe\" | \"Path\""`
// Pipe represents stdout or stderr. Ignored unless kind is Pipe.
Pipe string `json:"pipe,omitempty" yaml:"pipe,omitempty" cue:"string | *\"stdout\" | \"stderr\""`
// Path represents an artifact path relative to the task temp directory.
// Ignored unless kind is Path.
Path string `json:"path,omitempty" yaml:"path,omitempty"`
}
```
<a name="Platform"></a>
## type Platform {#Platform}
@@ -365,10 +455,13 @@ type PlatformSpec struct {
Repository represents a [Helm](<#Helm>) [Chart](<#Chart>) repository.
The Auth field is useful to configure http basic authentication to the Helm repository. Holos gets the username and password from the environment variables represented by the Auth field.
```go
type Repository struct {
Name string `json:"name" yaml:"name"`
URL string `json:"url" yaml:"url"`
Auth Auth `json:"auth,omitempty" yaml:"auth,omitempty"`
}
```
@@ -390,6 +483,20 @@ Resources represents Kubernetes resources. Most commonly used to mix resources i
type Resources map[Kind]map[InternalLabel]Resource
```
<a name="TaskData"></a>
## type TaskData {#TaskData}
TaskData represents data values associated with a pipeline task necessary to execute the task. For example, the randomly generated temporary directory used to read and write artifact files when executing user defined task commands. Values of this struct are intended for the Go template engine.
Holos populates this struct as needed. Holos may treat user provided values as an error condition.
```go
type TaskData struct {
// TempDir represents the temp directory holos manages for task artifacts.
TempDir string `json:"tempDir,omitempty" yaml:"tempDir,omitempty"`
}
```
<a name="Transformer"></a>
## type Transformer {#Transformer}
@@ -397,11 +504,12 @@ Transformer combines multiple inputs from prior [Generator](<#Generator>) or [Tr
1. [Kustomize](<#Kustomize>) \- Patch and transform the output from prior generators or transformers. See [Introduction to Kustomize](<https://kubectl.docs.kubernetes.io/guides/config_management/introduction/>).
2. [Join](<#Join>) \- Concatenate multiple prior outputs into one output.
3. \[Slice\] \- Slice an artifact into multiple artifacts using [kubectl\\\-slice](<https://github.com/patrickdappollonio/kubectl-slice>).
```go
type Transformer struct {
// Kind represents the kind of transformer. Must be Kustomize, or Join.
Kind string `json:"kind" yaml:"kind" cue:"\"Kustomize\" | \"Join\""`
Kind string `json:"kind" yaml:"kind" cue:"\"Kustomize\" | \"Join\" | \"Slice\""`
// Inputs represents the files to transform. The Output of prior Generators
// and Transformers.
Inputs []FilePath `json:"inputs" yaml:"inputs"`
@@ -431,6 +539,23 @@ type Validator struct {
}
```
<a name="ValueFile"></a>
## type ValueFile {#ValueFile}
ValueFile represents one Helm value file produced from CUE.
```go
type ValueFile struct {
// Name represents the file name, e.g. "region-values.yaml"
Name string `json:"name" yaml:"name"`
// Kind is a discriminator.
Kind string `json:"kind" yaml:"kind" cue:"\"Values\""`
// Values represents values for holos to marshal into the file name specified
// by Name when rendering the chart.
Values Values `json:"values,omitempty" yaml:"values,omitempty"`
}
```
<a name="Values"></a>
## type Values {#Values}

View File

@@ -0,0 +1,57 @@
---
description: Holos compared to other tools
sidebar_label: Comparison
slug: comparison
sidebar_position: 40
---
{/* cspell:ignore Prodan, rollouts */}
# Holos compared to other tools
## Timoni
Holos and Timoni both aim to solve similar problems but approach them at
different levels of the stack.
Timoni focuses on managing applications by evaluating [CUE] stored in OCI
containers. Its creator, Stephan Prodan, envisions a controller that applies the
resulting manifests. In this process, Timoni defers to [Flux] for managing Helm
charts within the cluster.
In contrast, Holos implements the [Rendered Manifests Pattern] and takes a
different approach, particularly in how it handles [Helm] charts. Like
[ArgoCD], Holos renders Helm charts into manifests using the `helm template`
command in its rendering pipeline. Holos differs from Timoni in several important
ways:
1. **Separation of Responsibilities:** Holos stops short of applying
rendered manifests to a cluster, leaving that task to existing tools like
[ArgoCD], [Flux], or even basic `kubectl apply` commands.
2. **Ecosystem Integration:** By focusing solely on rendering Kubernetes
manifests, Holos creates space for other tools to handle deployment and
management. For instance, Holos integrates seamlessly with [Kargo] for
progressive rollouts, as [Kargo] operates between Holos and the Kubernetes API.
This approach ensures that you're not locked into any specific tool and can
choose the best solution for each task.
3. **Platform Integration:** Holos focuses on integrating multiple Components
into a larger Platform. In Holos terminology, a Component refers to a wrapper
for [Helm] charts, [Kustomize] bases, or raw YAML files, integrated into the
rendering pipeline through [CUE]. A Platform represents the full combination of
these components.
4. **Explicit Rendering Pipeline:** Holos emphasizes flexibility in its
rendering pipeline. The system allows any tool that generates Kubernetes
manifests to be wrapped in a Generator, which can then feed into existing
transformers like [Kustomize]. This explicit separation makes Holos highly
adaptable for different workflows.
[Kargo]: https://kargo.io/
[Flux]: https://fluxcd.io
[Helm]: https://helm.sh
[ArgoCD]: https://argoproj.github.io/cd/
[Kustomize]: https://kustomize.io/
[CUE]: https://cuelang.org/
[Rendered Manifests Pattern]: https://akuity.io/blog/the-rendered-manifests-pattern

View File

@@ -0,0 +1,218 @@
---
slug: flux-kustomization
title: Flux Kustomization
description: Configuring a Kustomization for each Component.
sidebar_position: 120
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CommonComponent from '../../common/example-component.mdx';
import CommonComponentIntegrate from '../../common/example-component-integrate.mdx';
# Flux Kustomization
## Overview
This topic covers how to mix in a Flux Kustomization to all components. We'll
use the `Artifacts` field of [ComponentConfig] defined by the author schema.
## The Code
### Generating the structure
Use `holos` to generate a minimal platform directory structure. Start by
creating a blank directory to hold the platform configuration.
```shell
mkdir holos-flux-kustomization && cd holos-flux-kustomization
```
```shell
holos init platform v1alpha5
```
### Creating an example Component
<CommonComponent />
<CommonComponentIntegrate />
## Adding Flux Kustomizations
Configure Holos to render a [Kustomization] by defining an [Artifact] for it in
every BuildPlan holos produces. We're unifying our custom configuration with
the existing `#ComponentConfig` defined in `schema.cue`.
```bash
cat <<EOF >flux-kustomization.cue
```
```cue showLineNumbers
package holos
import (
"path"
flux "kustomize.toolkit.fluxcd.io/kustomization/v1"
)
#ComponentConfig: {
Name: _
OutputBaseDir: _
let ArtifactPath = path.Join([OutputBaseDir, "gitops", "\(Name).kustomization.gen.yaml"], path.Unix)
let ResourcesPath = path.Join(["deploy", OutputBaseDir, "components", Name], path.Unix)
Artifacts: "\(Name)-kustomization": {
artifact: ArtifactPath
generators: [{
kind: "Resources"
output: artifact
resources: Kustomization: (Name): flux.#Kustomization & {
metadata: name: Name
metadata: namespace: "default"
spec: {
interval: "5m"
timeout: "1m"
prune: true
path: ResourcesPath
sourceRef: {
kind: "GitRepository"
name: "webapp"
}
}
}
}]
}
}
```
```bash
EOF
```
## Inspecting the BuildPlan
Our customized `#ComponentConfig` results in the following `BuildPlan`.
:::note
The second artifact around line 40 contains the configured `Kustomization`
resource.
:::
<Tabs groupId="55075C71-02E8-4222-88C0-2D52C82D18FC">
<TabItem value="command" label="Command">
```bash
holos cue export --expression holos --out=yaml ./components/podinfo
```
</TabItem>
<TabItem value="output" label="Output">
```yaml showLineNumbers
kind: BuildPlan
apiVersion: v1alpha5
metadata:
name: podinfo
spec:
artifacts:
- artifact: components/podinfo/podinfo.gen.yaml
generators:
- kind: Helm
output: helm.gen.yaml
helm:
chart:
name: podinfo
version: 6.6.2
release: podinfo
repository:
name: podinfo
url: https://stefanprodan.github.io/podinfo
values:
ui:
message: Hello World
enableHooks: false
- kind: Resources
output: resources.gen.yaml
resources: {}
validators: []
transformers:
- kind: Kustomize
inputs:
- helm.gen.yaml
- resources.gen.yaml
output: components/podinfo/podinfo.gen.yaml
kustomize:
kustomization:
resources:
- helm.gen.yaml
- resources.gen.yaml
kind: Kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
- artifact: gitops/podinfo.kustomization.gen.yaml
generators:
- kind: Resources
output: gitops/podinfo.kustomization.gen.yaml
resources:
Kustomization:
podinfo:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: podinfo
namespace: default
spec:
interval: 5m
path: deploy/components/podinfo
prune: true
sourceRef:
kind: GitRepository
name: webapp
timeout: 1m
```
</TabItem>
</Tabs>
## Rendering manifests
<Tabs groupId="E150C802-7162-4FBF-82A7-77D9ADAEE847">
<TabItem value="command" label="Command">
```bash
holos render platform
```
</TabItem>
<TabItem value="output" label="Output">
```
rendered podinfo in 140.341417ms
rendered platform in 140.441333ms
```
</TabItem>
</Tabs>
## Reviewing the Kustomization
The Artifact we added to `#ComponentConfig` will produce a Flux Kustomization
resource for every component in the platform. The output in this example is
located at:
```txt
deploy/gitops/podinfo.kustomization.gen.yaml
```
```yaml showLineNumbers
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: podinfo
namespace: default
spec:
interval: 5m
path: deploy/components/podinfo
prune: true
sourceRef:
kind: GitRepository
name: webapp
timeout: 1m
```
[podinfo]: https://github.com/stefanprodan/podinfo
[CUE Module]: https://cuelang.org/docs/reference/modules/
[CUE Tags]: https://cuelang.org/docs/howto/inject-value-into-evaluation-using-tag-attribute/
[Kustomization]: https://fluxcd.io/flux/components/kustomize/kustomizations/
[Platform]: ../../api/author.md#Platform
[ComponentConfig]: ../../api/author.md#ComponentConfig
[Artifact]: ../../api/core.md#Artifact

20
doc/md/topics/kargo.mdx Normal file
View File

@@ -0,0 +1,20 @@
---
description: Kargo
slug: kargo
sidebar_position: 110
---
# Kargo
Holos pairs nicely with [Kargo], offering a holistic solution for code
promotion across stages.
Watch this space for a more detailed write up of the integration being
developed.
If you're interested in this topic, please thumbs up the [Kargo
Topic](https://github.com/holos-run/holos/issues/378) issue, or drop into
[Discord] and let us know about your use case.
[Kargo]: https://kargo.io/
[Discord]: https://discord.gg/JgDVbNpye7

View File

@@ -0,0 +1,65 @@
---
description: OCI Helm Charts
slug: oci-helm-charts
sidebar_position: 710
---
# OCI Helm Charts
Holos supports OCI Helm charts. Use the following example to get started.
```bash
mkdir -p oci-helm && cd oci-helm
holos init platform v1alpha5
```
```bash
mkdir -p components/podinfo-oci
cat <<EOF > components/podinfo-oci/podinfo-oci.cue
```
```cue showLineNumbers
package holos
holos: Component.BuildPlan
Component: #Helm & {
Chart: {
name: "oci://ghcr.io/stefanprodan/charts/podinfo"
release: "podinfo"
version: "6.6.2"
}
}
```
```bash
EOF
```
Register the component with the platform.
```bash
cat <<EOF >platform/podinfo-oci.cue
```
```cue showLineNumbers
package holos
Platform: Components: podinfo: {
name: "podinfo-oci"
path: "components/podinfo-oci"
}
```
```bash
EOF
```
The OCI chart is cached in the vendor directory and rendered.
```bash
holos render platform
```
```txt
Pulled: ghcr.io/stefanprodan/charts/podinfo:6.6.2
Digest: sha256:83295d47de6d6ca634ed4b952a7572fc176bcc38854d0c11ca0fa197bc5f1154
rendered podinfo-oci in 7.21581325s
rendered platform in 7.216199167s
```

View File

@@ -0,0 +1,183 @@
---
description: Private Helm Repositories
slug: private-helm
sidebar_position: 700
---
# Private Helm
Holos supports private Helm repositories accessed with http basic authentication
since `v0.101.4`. Use the following command to update your author and core
schemas to support this configuration.
```bash
holos init platform v1alpha5 --force
```
## Configuration
Holos uses the Helm SDK and defers to it for authentication to private
repositories. Each Helm Generator supports providing http basic authentication
credentials from environment variables.
For example, the following BuildPlan causes `holos` to get the admin username
password from the `HOLOS_TEST_PASS` environment variable.
```bash
mkdir -p projects/holos/components/private-chart
cat <<EOF > projects/holos/components/private-chart/private-chart.cue
```
```cue showLineNumbers
package holos
holos: Component.BuildPlan
// Test holos can access a private repository with basic auth.
// https://github.com/holos-run/holos/issues/370
Component: #Helm & {
Chart: {
name: "mychart"
version: "0.1.0"
repository: {
name: "holos-test"
url: "https://charts.holos.localhost"
// auth: username: fromEnv: "HOLOS_TEST_USER"
auth: username: value: "admin"
auth: password: fromEnv: "HOLOS_TEST_PASS"
}
}
}
```
```bash
EOF
```
## Verification
Verify `holos` can access a private Helm repository by setting [ChartMuseum] up
on a [Local Cluster]. We'll use https with basic auth to authenticate to the
chart repository.
Using the [bank of holos] repository, deploy chart museum:
```bash
holos render platform -t ChartMuseum
```
Apply the manifests:
```bash
kubectl apply --server-side=true -f deploy/clusters/workload/projects/holos/components/chart-museum
kubectl apply --server-side=true -f deploy/clusters/workload/projects/network/components/httproutes
```
Get the admin password:
```bash
kubectl get secret -n holos chartmuseum-auth -o json \
| jq --exit-status -r '.data.password | @base64d'
```
Add a local repo:
```bash
helm repo add holos-test https://charts.holos.localhost --username admin
```
```txt
Password:
"holos-test" has been added to your repositories
```
:::note
Helm by default stores this password in `~/Library/Preferences/helm/repositories.yaml`
:::
Create a chart:
```bash
helm create mychart
```
```txt
Creating mychart
```
Package it up.
```bash
helm package mychart
```
```txt
Successfully packaged chart and saved it to: mychart-0.1.0.tgz
```
Publish it.
```bash
curl --user "admin:$(pbpaste)" --data-binary "@mychart-0.1.0.tgz" https://charts.holos.localhost/api/charts
```
```json
{"saved":true}
```
Remove all cached charts:
```bash
find . -name vendor | xargs rm -rf
```
Render the chart:
```bash
cat <<EOF > test-private-repo.cue
```
```cue showLineNumbers
@if(TestPrivateRepo)
package holos
// Test holos can access a private repository with basic auth.
// https://github.com/holos-run/holos/issues/370
Projects: holos: #ProjectBuilder & {
team: "holos-authors"
namespaces: holos: _
_components: "private-chart": _
}
```
```bash
EOF
```
```
time holos render platform -t TestPrivateRepo
```
Check the chart was pulled and cached:
```shell
tree ./projects/holos/components/private-chart/vendor
```
```txt
./projects/holos/components/private-chart/vendor
└── 0.1.0
├── mychart
│   ├── Chart.yaml
│   ├── mychart-0.1.0.tgz
│   ├── templates
│   │   ├── NOTES.txt
│   │   ├── _helpers.tpl
│   │   ├── deployment.yaml
│   │   ├── hpa.yaml
│   │   ├── ingress.yaml
│   │   ├── service.yaml
│   │   ├── serviceaccount.yaml
│   │   └── tests
│   │   └── test-connection.yaml
│   └── values.yaml
└── mychart-0.1.0.tgz
6 directories, 11 files
```
[Local Cluster]: ./local-cluster.mdx
[ChartMuseum]: https://chartmuseum.com/docs/
[bank of holos]: https://github.com/holos-run/bank-of-holos

View File

@@ -45,7 +45,40 @@ holos init platform v1alpha5
### Using an example Component
<CommonComponent />
Create a directory for the example `podinfo` component we'll use to render
platform manifests.
```bash
mkdir -p components/podinfo
```
Create the CUE configuration for the example `podinfo` component.
```bash
cat <<EOF >components/podinfo/podinfo.cue
```
```cue showLineNumbers
package holos
holos: Component.BuildPlan
Component: #Helm & {
Chart: {
name: "podinfo"
version: "6.6.2"
repository: {
name: "podinfo"
url: "https://stefanprodan.github.io/podinfo"
}
}
Values: ui: {
message: string | *"Hello World" @tag(message, type=string)
}
}
```
```bash
EOF
```
We'll integrate the component with the platform after we define the
configuration structures.
@@ -277,6 +310,9 @@ let ProdPodinfo = {
parameters: EnvironmentName: "prod-\(_city)"
}
```
```
EOF
```
### Using the environment

View File

@@ -0,0 +1,7 @@
exec bash -c 'bash -euo pipefail $WORK/command.sh 2>&1'
cmp stdout $WORK/output.txt
-- command.sh --
holos --version
-- output.txt --
0.104.1

View File

@@ -0,0 +1,126 @@
# Set $HOME because:
# - Helm uses it for temporary files
# - Git requires it for setting author name/email globally
env HOME=$WORK/.tmp
chmod 0755 $WORK/update.sh
# Configure git author for testscript execution
exec git config --global user.name 'Holos Docs'
exec git config --global user.email 'hello@holos.run'
exec git config --global init.defaultBranch main
# Remove the tutorial directory if it already exists
exec rm -rf holos-tutorial
# Create and change to the tutorial directory, and then initialize the Holos platform
exec bash -c 'bash -euo pipefail $WORK/mkdir-and-init.sh'
cd holos-tutorial
# Create the components directory
exec bash -c 'bash -euo pipefail $WORK/mkdir-components.sh'
# Combine and execute the multiline podinfo component header/body/trailer files
exec cat $WORK/podinfo-component-header.sh ../podinfo-component-body.cue ../eof-trailer.sh
stdin stdout
exec bash -xeuo pipefail
# Combine and execute the multiline platform registration header/body/trailer files
exec cat $WORK/register-podinfo-header.sh ../register-podinfo-body.cue ../eof-trailer.sh
stdin stdout
exec bash -xeuo pipefail
# Render the platform, capture stdout, and use update.sh to gate whether the
# output file should be updated.
#
# NOTE: The [net] condition will test whether external network access is available
[net] exec bash -c 'bash -euo pipefail $WORK/render.sh 2>&1'
[net] stdin stdout
exec $WORK/update.sh $WORK/register-components-output.txt
# Generate and update the tree of the tutorial directory (omitting the cue.mod directory)
exec bash -c 'bash -euo pipefail $WORK/tree.sh'
stdin stdout
exec $WORK/update.sh $WORK/tree.txt
# Split the rendered manifest into two separate files to display separately
exec bash -c 'bash -euo pipefail $WORK/split-rendered-manifest.sh $WORK/holos-tutorial/deploy/components/podinfo/podinfo.gen.yaml $WORK'
# Grep for the Hello Holos message and write the output file
exec bash -c 'bash -euo pipefail $WORK/grep-for-message.sh'
stdin stdout
exec $WORK/update.sh $WORK/grepped-output.txt
# Clean up the tutorial directory and tmp $HOME directory
cd $WORK
exec rm -rf holos-tutorial
exec rm -rf $HOME
-- update.sh --
#! /bin/bash
set -euo pipefail
[[ -s "$1" ]] && [[ -z "${HOLOS_UPDATE_SCRIPTS:-}" ]] && exit 0
cat > "$1"
-- mkdir-and-init.sh --
mkdir holos-tutorial && cd holos-tutorial
holos init platform v1alpha5
-- tree.sh --
tree -L 3 -I cue.mod .
-- mkdir-components.sh --
mkdir -p components/podinfo
-- podinfo-component-header.sh --
cat <<EOF > components/podinfo/podinfo.cue
-- podinfo-component-body.cue --
package holos
// Produce a helm chart build plan.
holos: HelmChart.BuildPlan
HelmChart: #Helm & {
Name: "podinfo"
Chart: {
version: "6.6.2"
repository: {
name: "podinfo"
url: "https://stefanprodan.github.io/podinfo"
}
}
// Holos marshals Values into values.yaml for Helm.
Values: {
// message is a string with a default value. @tag indicates a value may
// be injected from the platform spec component parameters.
ui: {
message: string | *"Hello World" @tag(greeting, type=string)
}
}
}
-- eof-trailer.sh --
EOF
-- register-podinfo-header.sh --
cat <<EOF > platform/podinfo.cue
-- register-podinfo-body.cue --
package holos
Platform: Components: podinfo: {
name: "podinfo"
path: "components/podinfo"
// Inject a value into the component.
parameters: greeting: "Hello Holos!"
}
-- render.sh --
holos render platform
-- register-components-output.txt --
cached podinfo 6.6.2
rendered podinfo in 1.938665041s
rendered platform in 1.938759417s
-- podinfo-rendered-path.sh --
deploy/components/podinfo/podinfo.gen.yaml
-- split-rendered-manifest.sh --
awk 'BEGIN {RS="---"} NR==1 {print > "service.yaml"} NR==2 {print > "deployment.yaml"}' $1
mv service.yaml $2/rendered-service.yaml
mv deployment.yaml $2/rendered-deployment.yaml
-- grep-for-message.sh --
grep -B2 Hello deploy/components/podinfo/podinfo.gen.yaml
-- grepped-output.txt --
env:
- name: PODINFO_UI_MESSAGE
value: Hello Holos!

View File

@@ -0,0 +1 @@
holos --version

View File

@@ -0,0 +1 @@
0.104.1

View File

@@ -0,0 +1 @@
EOF

View File

@@ -0,0 +1 @@
grep -B2 Hello deploy/components/podinfo/podinfo.gen.yaml

View File

@@ -0,0 +1,3 @@
env:
- name: PODINFO_UI_MESSAGE
value: Hello Holos!

View File

@@ -0,0 +1,2 @@
mkdir holos-tutorial && cd holos-tutorial
holos init platform v1alpha5

View File

@@ -0,0 +1 @@
mkdir -p components/podinfo

View File

@@ -0,0 +1,23 @@
package holos
// Produce a helm chart build plan.
holos: HelmChart.BuildPlan
HelmChart: #Helm & {
Name: "podinfo"
Chart: {
version: "6.6.2"
repository: {
name: "podinfo"
url: "https://stefanprodan.github.io/podinfo"
}
}
// Holos marshals Values into values.yaml for Helm.
Values: {
// message is a string with a default value. @tag indicates a value may
// be injected from the platform spec component parameters.
ui: {
message: string | *"Hello World" @tag(greeting, type=string)
}
}
}

View File

@@ -0,0 +1 @@
cat <<EOF > components/podinfo/podinfo.cue

View File

@@ -0,0 +1 @@
deploy/components/podinfo/podinfo.gen.yaml

View File

@@ -0,0 +1,2 @@
rendered podinfo in 312.472625ms
rendered platform in 312.557375ms

View File

@@ -0,0 +1,8 @@
package holos
Platform: Components: podinfo: {
name: "podinfo"
path: "components/podinfo"
// Inject a value into the component.
parameters: greeting: "Hello Holos!"
}

View File

@@ -0,0 +1 @@
cat <<EOF > platform/podinfo.cue

View File

@@ -0,0 +1 @@
holos render platform

View File

@@ -0,0 +1,93 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: podinfo
app.kubernetes.io/version: 6.6.2
helm.sh/chart: podinfo-6.6.2
name: podinfo
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: podinfo
strategy:
rollingUpdate:
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
annotations:
prometheus.io/port: "9898"
prometheus.io/scrape: "true"
labels:
app.kubernetes.io/name: podinfo
spec:
containers:
- command:
- ./podinfo
- --port=9898
- --cert-path=/data/cert
- --port-metrics=9797
- --grpc-port=9999
- --grpc-service-name=podinfo
- --level=info
- --random-delay=false
- --random-error=false
env:
- name: PODINFO_UI_MESSAGE
value: Hello Holos!
- name: PODINFO_UI_COLOR
value: '#34577c'
image: ghcr.io/stefanprodan/podinfo:6.6.2
imagePullPolicy: IfNotPresent
livenessProbe:
exec:
command:
- podcli
- check
- http
- localhost:9898/healthz
failureThreshold: 3
initialDelaySeconds: 1
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: podinfo
ports:
- containerPort: 9898
name: http
protocol: TCP
- containerPort: 9797
name: http-metrics
protocol: TCP
- containerPort: 9999
name: grpc
protocol: TCP
readinessProbe:
exec:
command:
- podcli
- check
- http
- localhost:9898/readyz
failureThreshold: 3
initialDelaySeconds: 1
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
resources:
limits: null
requests:
cpu: 1m
memory: 16Mi
volumeMounts:
- mountPath: /data
name: data
terminationGracePeriodSeconds: 30
volumes:
- emptyDir: {}
name: data

View File

@@ -0,0 +1,23 @@
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: podinfo
app.kubernetes.io/version: 6.6.2
helm.sh/chart: podinfo-6.6.2
name: podinfo
spec:
ports:
- name: http
port: 9898
protocol: TCP
targetPort: http
- name: grpc
port: 9999
protocol: TCP
targetPort: grpc
selector:
app.kubernetes.io/name: podinfo
type: ClusterIP

View File

@@ -0,0 +1,3 @@
awk 'BEGIN {RS="---"} NR==1 {print > "service.yaml"} NR==2 {print > "deployment.yaml"}' $1
mv service.yaml $2/rendered-service.yaml
mv deployment.yaml $2/rendered-deployment.yaml

View File

@@ -0,0 +1 @@
tree -L 3 -I cue.mod .

View File

@@ -0,0 +1,17 @@
.
|-- components
| `-- podinfo
| |-- podinfo.cue
| `-- vendor
|-- deploy
| `-- components
| `-- podinfo
|-- platform
| |-- platform.gen.cue
| `-- podinfo.cue
|-- platform.metadata.json
|-- resources.cue
|-- schema.cue
`-- tags.cue
8 directories, 7 files

View File

@@ -0,0 +1,4 @@
#! /bin/bash
set -euo pipefail
[[ -s "$1" ]] && [[ -z "${HOLOS_UPDATE_SCRIPTS:-}" ]] && exit 0
cat > "$1"

View File

@@ -0,0 +1,7 @@
exec bash -c 'bash -euo pipefail $WORK/command.sh 2>&1'
cmp stdout $WORK/output.txt
-- command.sh --
holos --version
-- output.txt --
0.104.1

View File

@@ -0,0 +1,374 @@
# Set $HOME because:
# - Helm uses it for temporary files
# - Git requires it for setting author name/email globally
env HOME=$WORK/.tmp
chmod 0755 $WORK/update.sh
# Configure git author for testscript execution
exec git config --global user.name 'Holos Docs'
exec git config --global user.email 'hello@holos.run'
exec git config --global init.defaultBranch main
# Remove the tutorial directory if it already exists
exec rm -rf holos-helm-values-tutorial
# Create and change to the tutorial directory, and then initialize the Holos platform
exec bash -c 'bash -euo pipefail mkdir-and-init.sh'
cd holos-helm-values-tutorial
# Git init and create the component directories
exec bash -c 'bash -euo pipefail $WORK/git-init.sh'
exec bash -c 'bash -euo pipefail $WORK/mkdir-components.sh'
# Combine and execute the multiline prometheus/blackbox component header/body/trailer files
exec cat $WORK/prometheus-component-header.sh ../prometheus-component-body.cue ../eof-trailer.sh
stdin stdout
exec bash -xeuo pipefail
exec cat $WORK/blackbox-component-header.sh ../blackbox-component-body.cue ../eof-trailer.sh
stdin stdout
exec bash -xeuo pipefail
# Combine and execute the multiline platform registration header/body/trailer files.
exec cat $WORK/register-components-header.sh ../register-components-body.cue ../eof-trailer.sh
stdin stdout
exec bash -xeuo pipefail
# Render the platform, capture stdout, and use update.sh to gate whether the
# output file should be updated.
#
# NOTE: The [net] condition will test whether external network access is available
[net] exec bash -c 'bash -euo pipefail $WORK/render.sh 2>&1'
[net] stdin stdout
exec $WORK/update.sh $WORK/register-components-output.txt
# Commit and conditionally update the output file
exec bash -c 'bash -euo pipefail $WORK/register-components-git-commit.sh'
stdin stdout
exec $WORK/update.sh $WORK/register-components-git-commit-output.txt
# Import values
exec bash -c 'bash -euo pipefail $WORK/import-prometheus-values.sh'
exec bash -c 'bash -euo pipefail $WORK/import-blackbox-values.sh'
# Render, update the output file, commit, and update the commit output file.
[net] exec bash -c 'bash -euo pipefail $WORK/render.sh 2>&1'
[net] stdin stdout
exec $WORK/update.sh $WORK/import-values-render-output.txt
exec bash -c 'bash -euo pipefail $WORK/import-values-git-commit.sh'
stdin stdout
exec $WORK/update.sh $WORK/import-values-git-output.txt
# Create the common configuration path
exec bash -c 'bash -euo pipefail $WORK/mkdir-common-config.sh'
# Combine and execute the common configuration header/body/trailer to write the cue file.
exec cat $WORK/blackbox-common-config-header.sh ../blackbox-common-config-body.cue ../eof-trailer.sh
stdin stdout
exec bash -xeuo pipefail
# Git commit blackbox common config
exec bash -c 'bash -euo pipefail $WORK/blackbox-common-config-git-commit.sh'
stdin stdout
exec $WORK/update.sh $WORK/blackbox-common-config-git-output.txt
# Patch the common config values file and write to output file.
#
# NOTE: Using a symlink here because the patch script references values.patch
# within the same directory, but it actually lives one directory up in the
# testscript $WORK dir.
exec ln -s $WORK/values.patch values.patch
exec bash -c 'bash -euo pipefail $WORK/common-config-patch.sh'
stdin stdout
exec $WORK/update.sh $WORK/common-config-patch.txt
# Remove patch and commit changes
exec bash -c 'bash -euo pipefail $WORK/common-config-rm.sh'
exec bash -c 'bash -euo pipefail $WORK/common-config-git.sh'
stdin stdout
exec $WORK/update.sh $WORK/common-config-git-output.txt
# Final render and update of output file.
[net] exec bash -c 'bash -euo pipefail $WORK/render.sh 2>&1'
[net] stdin stdout
exec $WORK/update.sh $WORK/reviewing-changes-git-output.txt
# Git diff and write to output file.
exec bash -c 'bash -euo pipefail $WORK/git-diff.sh'
stdin stdout
exec $WORK/update.sh $WORK/git.diff
# Final commit and write to output file
exec bash -c 'bash -euo pipefail $WORK/reviewing-changes-git-commit.sh'
stdin stdout
exec $WORK/update.sh $WORK/reviewing-changes-git-output.txt
# Clean up the tutorial directory and tmp $HOME directory
cd $WORK
exec rm -rf holos-helm-values-tutorial
exec rm -rf $HOME
-- update.sh --
#! /bin/bash
set -euo pipefail
[[ -s "$1" ]] && [[ -z "${HOLOS_UPDATE_SCRIPTS:-}" ]] && exit 0
cat > "$1"
-- mkdir-and-init.sh --
mkdir holos-helm-values-tutorial
cd holos-helm-values-tutorial
holos init platform v1alpha5
-- git-init.sh --
git init . && git add . && git commit -m "initial commit"
-- mkdir-components.sh --
mkdir -p components/prometheus components/blackbox
-- prometheus-component-header.sh --
cat <<EOF > components/prometheus/prometheus.cue
-- prometheus-component-body.cue --
package holos
// Produce a helm chart build plan.
holos: Helm.BuildPlan
Helm: #Helm & {
Chart: {
name: "prometheus"
version: "25.27.0"
repository: {
name: "prometheus-community"
url: "https://prometheus-community.github.io/helm-charts"
}
}
}
-- eof-trailer.sh --
EOF
-- blackbox-component-header.sh --
cat <<EOF > components/blackbox/blackbox.cue
-- blackbox-component-body.cue --
package holos
// Produce a helm chart build plan.
holos: Helm.BuildPlan
Helm: #Helm & {
Chart: {
name: "prometheus-blackbox-exporter"
version: "9.0.1"
repository: {
name: "prometheus-community"
url: "https://prometheus-community.github.io/helm-charts"
}
}
}
-- register-components-header.sh --
cat <<EOF > platform/prometheus.cue
-- register-components-body.cue --
package holos
Platform: Components: {
prometheus: {
name: "prometheus"
path: "components/prometheus"
}
blackbox: {
name: "blackbox"
path: "components/blackbox"
}
}
-- render.sh --
holos render platform
-- register-components-output.txt --
cached prometheus-blackbox-exporter 9.0.1
rendered blackbox in 3.825430417s
cached prometheus 25.27.0
rendered prometheus in 4.840089667s
rendered platform in 4.840137792s
-- register-components-git-commit.sh --
git add . && git commit -m 'add blackbox and prometheus'
-- register-components-git-commit-output.txt --
[main b5df111] add blackbox and prometheus
5 files changed, 1550 insertions(+)
create mode 100644 components/blackbox/blackbox.cue
create mode 100644 components/prometheus/prometheus.cue
create mode 100644 deploy/components/blackbox/blackbox.gen.yaml
create mode 100644 deploy/components/prometheus/prometheus.gen.yaml
create mode 100644 platform/prometheus.cue
-- import-prometheus-values.sh --
holos cue import \
--package holos \
--path 'Helm: Values:' \
--outfile components/prometheus/values.cue \
components/prometheus/vendor/25.27.0/prometheus/values.yaml
-- import-blackbox-values.sh --
holos cue import \
--package holos \
--path 'Helm: Values:' \
--outfile components/blackbox/values.cue \
components/blackbox/vendor/9.0.1/prometheus-blackbox-exporter/values.yaml
-- import-values-render-output.txt --
rendered blackbox in 365.936792ms
rendered prometheus in 371.855875ms
rendered platform in 372.109916ms
-- import-values-git-commit.sh --
git add . && git commit -m 'import values'
-- import-values-git-output.txt --
[main 52e90ea] import values
2 files changed, 1815 insertions(+)
create mode 100644 components/blackbox/values.cue
create mode 100644 components/prometheus/values.cue
-- mkdir-common-config.sh --
mkdir -p config/prometheus
-- blackbox-common-config-header.sh --
cat <<EOF > config/prometheus/blackbox.cue
-- blackbox-common-config-body.cue --
package prometheus
// Schema Definition
#Blackbox: {
// host constrained to a lower case dns label
host: string & =~"^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"
// port constrained to a valid range
port: int & >0 & <=65535
}
// Concrete values must validate against the schema.
blackbox: #Blackbox & {
host: "blackbox"
port: 9115
}
-- blackbox-common-config-git-commit.sh --
git add . && git commit -m 'add blackbox configuration'
-- blackbox-common-config-git-output.txt --
[main 1adcd08] add blackbox configuration
1 file changed, 15 insertions(+)
create mode 100644 components/blackbox.cue
-- common-config-patch.sh --
patch -p1 < values.patch
-- values.patch --
--- a/components/blackbox/values.cue
+++ b/components/blackbox/values.cue
@@ -1,6 +1,11 @@
package holos
+// Import common blackbox configuration
+import "holos.example/config/prometheus"
+
Helm: Values: {
+ fullnameOverride: prometheus.blackbox.host
+
global: {
//# Global image registry to use if it needs to be overriden for some specific use cases (e.g local registries, custom images, ...)
//#
@@ -192,7 +197,7 @@ Helm: Values: {
annotations: {}
labels: {}
type: "ClusterIP"
- port: 9115
+ port: prometheus.blackbox.port
ipDualStack: {
enabled: false
ipFamilies: ["IPv6", "IPv4"]
--- a/components/prometheus/values.cue
+++ b/components/prometheus/values.cue
@@ -1,5 +1,8 @@
package holos
+// Import common blackbox configuration
+import "holos.example/config/prometheus"
+
Helm: Values: {
// yaml-language-server: $schema=values.schema.json
// Default values for prometheus.
@@ -1083,7 +1086,7 @@ Helm: Values: {
target_label: "__param_target"
}, {
target_label: "__address__"
- replacement: "blackbox"
+ replacement: "\(prometheus.blackbox.host):\(prometheus.blackbox.port)"
}, {
source_labels: ["__param_target"]
target_label: "instance"
-- common-config-patch.txt --
patching file 'components/blackbox/values.cue'
patching file 'components/prometheus/values.cue'
-- common-config-rm.sh --
rm values.patch
-- common-config-git.sh --
git add . && git commit -m 'integrate blackbox and prometheus together'
-- common-config-git-output.txt --
[main 4221803] integrate blackbox and prometheus together
2 files changed, 4 insertions(+), 2 deletions(-)
-- reviewing-changes-render-output.txt --
rendered blackbox in 374.810666ms
rendered prometheus in 382.899334ms
rendered platform in 383.270625ms
-- git-diff.sh --
git diff
-- git.diff --
diff --git a/deploy/components/blackbox/blackbox.gen.yaml b/deploy/components/blackbox/blackbox.gen.yaml
index 3db20cd..5336f44 100644
--- a/deploy/components/blackbox/blackbox.gen.yaml
+++ b/deploy/components/blackbox/blackbox.gen.yaml
@@ -7,7 +7,7 @@ metadata:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/version: v0.25.0
helm.sh/chart: prometheus-blackbox-exporter-9.0.1
- name: prometheus-blackbox-exporter
+ name: blackbox
namespace: default
---
apiVersion: v1
@@ -31,7 +31,7 @@ metadata:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/version: v0.25.0
helm.sh/chart: prometheus-blackbox-exporter-9.0.1
- name: prometheus-blackbox-exporter
+ name: blackbox
namespace: default
---
apiVersion: v1
@@ -43,7 +43,7 @@ metadata:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/version: v0.25.0
helm.sh/chart: prometheus-blackbox-exporter-9.0.1
- name: prometheus-blackbox-exporter
+ name: blackbox
namespace: default
spec:
ports:
@@ -65,7 +65,7 @@ metadata:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/version: v0.25.0
helm.sh/chart: prometheus-blackbox-exporter-9.0.1
- name: prometheus-blackbox-exporter
+ name: blackbox
namespace: default
spec:
replicas: 1
@@ -119,8 +119,8 @@ spec:
name: config
hostNetwork: false
restartPolicy: Always
- serviceAccountName: prometheus-blackbox-exporter
+ serviceAccountName: blackbox
volumes:
- configMap:
- name: prometheus-blackbox-exporter
+ name: blackbox
name: config
diff --git a/deploy/components/prometheus/prometheus.gen.yaml b/deploy/components/prometheus/prometheus.gen.yaml
index 9e02bce..ab638f0 100644
--- a/deploy/components/prometheus/prometheus.gen.yaml
+++ b/deploy/components/prometheus/prometheus.gen.yaml
@@ -589,7 +589,7 @@ data:
- source_labels:
- __address__
target_label: __param_target
- - replacement: blackbox
+ - replacement: blackbox:9115
target_label: __address__
- source_labels:
- __param_target
-- reviewing-changes-git-commit.sh --
git add . && git commit -m 'render integrated blackbox and prometheus manifests'
-- reviewing-changes-git-output.txt --
[main 67efe0d] render integrated blackbox and prometheus manifests
2 files changed, 7 insertions(+), 7 deletions(-)

View File

@@ -0,0 +1 @@
holos --version

View File

@@ -0,0 +1 @@
0.104.1

View File

@@ -0,0 +1,15 @@
package prometheus
// Schema Definition
#Blackbox: {
// host constrained to a lower case dns label
host: string & =~"^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"
// port constrained to a valid range
port: int & >0 & <=65535
}
// Concrete values must validate against the schema.
blackbox: #Blackbox & {
host: "blackbox"
port: 9115
}

View File

@@ -0,0 +1 @@
git add . && git commit -m 'add blackbox configuration'

View File

@@ -0,0 +1,3 @@
[main aa9749e] add blackbox configuration
1 file changed, 15 insertions(+)
create mode 100644 config/prometheus/blackbox.cue

View File

@@ -0,0 +1 @@
cat <<EOF > config/prometheus/blackbox.cue

View File

@@ -0,0 +1,15 @@
package holos
// Produce a helm chart build plan.
holos: Helm.BuildPlan
Helm: #Helm & {
Chart: {
name: "prometheus-blackbox-exporter"
version: "9.0.1"
repository: {
name: "prometheus-community"
url: "https://prometheus-community.github.io/helm-charts"
}
}
}

View File

@@ -0,0 +1 @@
cat <<EOF > components/blackbox/blackbox.cue

View File

@@ -0,0 +1,3 @@
[main 656d4ee] integrate blackbox and prometheus together
3 files changed, 1348 insertions(+), 2 deletions(-)
create mode 100644 components/prometheus/values.cue.orig

View File

@@ -0,0 +1 @@
git add . && git commit -m 'integrate blackbox and prometheus together'

View File

@@ -0,0 +1 @@
patch -p1 < values.patch

View File

@@ -0,0 +1,2 @@
patching file 'components/blackbox/values.cue'
patching file 'components/prometheus/values.cue'

View File

@@ -0,0 +1 @@
rm values.patch

View File

@@ -0,0 +1 @@
EOF

View File

@@ -0,0 +1 @@
git diff

View File

@@ -0,0 +1 @@
git init . && git add . && git commit -m "initial commit"

View File

@@ -0,0 +1,64 @@
diff --git a/deploy/components/blackbox/blackbox.gen.yaml b/deploy/components/blackbox/blackbox.gen.yaml
index 3db20cd..5336f44 100644
--- a/deploy/components/blackbox/blackbox.gen.yaml
+++ b/deploy/components/blackbox/blackbox.gen.yaml
@@ -7,7 +7,7 @@ metadata:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/version: v0.25.0
helm.sh/chart: prometheus-blackbox-exporter-9.0.1
- name: prometheus-blackbox-exporter
+ name: blackbox
namespace: default
---
apiVersion: v1
@@ -31,7 +31,7 @@ metadata:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/version: v0.25.0
helm.sh/chart: prometheus-blackbox-exporter-9.0.1
- name: prometheus-blackbox-exporter
+ name: blackbox
namespace: default
---
apiVersion: v1
@@ -43,7 +43,7 @@ metadata:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/version: v0.25.0
helm.sh/chart: prometheus-blackbox-exporter-9.0.1
- name: prometheus-blackbox-exporter
+ name: blackbox
namespace: default
spec:
ports:
@@ -65,7 +65,7 @@ metadata:
app.kubernetes.io/name: prometheus-blackbox-exporter
app.kubernetes.io/version: v0.25.0
helm.sh/chart: prometheus-blackbox-exporter-9.0.1
- name: prometheus-blackbox-exporter
+ name: blackbox
namespace: default
spec:
replicas: 1
@@ -119,8 +119,8 @@ spec:
name: config
hostNetwork: false
restartPolicy: Always
- serviceAccountName: prometheus-blackbox-exporter
+ serviceAccountName: blackbox
volumes:
- configMap:
- name: prometheus-blackbox-exporter
+ name: blackbox
name: config
diff --git a/deploy/components/prometheus/prometheus.gen.yaml b/deploy/components/prometheus/prometheus.gen.yaml
index 9e02bce..ab638f0 100644
--- a/deploy/components/prometheus/prometheus.gen.yaml
+++ b/deploy/components/prometheus/prometheus.gen.yaml
@@ -589,7 +589,7 @@ data:
- source_labels:
- __address__
target_label: __param_target
- - replacement: blackbox
+ - replacement: blackbox:9115
target_label: __address__
- source_labels:
- __param_target

View File

@@ -0,0 +1,5 @@
holos cue import \
--package holos \
--path 'Helm: Values:' \
--outfile components/blackbox/values.cue \
components/blackbox/vendor/9.0.1/prometheus-blackbox-exporter/values.yaml

View File

@@ -0,0 +1,5 @@
holos cue import \
--package holos \
--path 'Helm: Values:' \
--outfile components/prometheus/values.cue \
components/prometheus/vendor/25.27.0/prometheus/values.yaml

View File

@@ -0,0 +1 @@
git add . && git commit -m 'import values'

View File

@@ -0,0 +1,4 @@
[main 3788921] import values
2 files changed, 1815 insertions(+)
create mode 100644 components/blackbox/values.cue
create mode 100644 components/prometheus/values.cue

View File

@@ -0,0 +1,3 @@
rendered blackbox in 129.142708ms
rendered prometheus in 165.260375ms
rendered platform in 165.33ms

View File

@@ -0,0 +1,3 @@
mkdir holos-helm-values-tutorial
cd holos-helm-values-tutorial
holos init platform v1alpha5

View File

@@ -0,0 +1 @@
mkdir -p config/prometheus

View File

@@ -0,0 +1 @@
mkdir -p components/prometheus components/blackbox

View File

@@ -0,0 +1,15 @@
package holos
// Produce a helm chart build plan.
holos: Helm.BuildPlan
Helm: #Helm & {
Chart: {
name: "prometheus"
version: "25.27.0"
repository: {
name: "prometheus-community"
url: "https://prometheus-community.github.io/helm-charts"
}
}
}

View File

@@ -0,0 +1 @@
cat <<EOF > components/prometheus/prometheus.cue

View File

@@ -0,0 +1,12 @@
package holos
Platform: Components: {
prometheus: {
name: "prometheus"
path: "components/prometheus"
}
blackbox: {
name: "blackbox"
path: "components/blackbox"
}
}

View File

@@ -0,0 +1,7 @@
[main 654ae06] add blackbox and prometheus
5 files changed, 1550 insertions(+)
create mode 100644 components/blackbox/blackbox.cue
create mode 100644 components/prometheus/prometheus.cue
create mode 100644 deploy/components/blackbox/blackbox.gen.yaml
create mode 100644 deploy/components/prometheus/prometheus.gen.yaml
create mode 100644 platform/prometheus.cue

View File

@@ -0,0 +1 @@
git add . && git commit -m 'add blackbox and prometheus'

View File

@@ -0,0 +1 @@
cat <<EOF > platform/prometheus.cue

View File

@@ -0,0 +1,3 @@
rendered blackbox in 1.096663084s
rendered prometheus in 1.151784875s
rendered platform in 1.151839916s

View File

@@ -0,0 +1 @@
holos render platform

View File

@@ -0,0 +1 @@
git add . && git commit -m 'render integrated blackbox and prometheus manifests'

View File

@@ -0,0 +1,2 @@
[main 9bc0126] render integrated blackbox and prometheus manifests
2 files changed, 7 insertions(+), 7 deletions(-)

View File

@@ -0,0 +1,3 @@
rendered blackbox in 374.810666ms
rendered prometheus in 382.899334ms
rendered platform in 383.270625ms

View File

@@ -0,0 +1,4 @@
#! /bin/bash
set -euo pipefail
[[ -s "$1" ]] && [[ -z "${HOLOS_UPDATE_SCRIPTS:-}" ]] && exit 0
cat > "$1"

View File

@@ -0,0 +1,43 @@
--- a/components/blackbox/values.cue
+++ b/components/blackbox/values.cue
@@ -1,6 +1,11 @@
package holos
+// Import common blackbox configuration
+import "holos.example/config/prometheus"
+
Helm: Values: {
+ fullnameOverride: prometheus.blackbox.host
+
global: {
//# Global image registry to use if it needs to be overriden for some specific use cases (e.g local registries, custom images, ...)
//#
@@ -192,7 +197,7 @@ Helm: Values: {
annotations: {}
labels: {}
type: "ClusterIP"
- port: 9115
+ port: prometheus.blackbox.port
ipDualStack: {
enabled: false
ipFamilies: ["IPv6", "IPv4"]
--- a/components/prometheus/values.cue
+++ b/components/prometheus/values.cue
@@ -1,5 +1,8 @@
package holos
+// Import common blackbox configuration
+import "holos.example/config/prometheus"
+
Helm: Values: {
// yaml-language-server: $schema=values.schema.json
// Default values for prometheus.
@@ -1083,7 +1086,7 @@ Helm: Values: {
target_label: "__param_target"
}, {
target_label: "__address__"
- replacement: "blackbox"
+ replacement: "\(prometheus.blackbox.host):\(prometheus.blackbox.port)"
}, {
source_labels: ["__param_target"]
target_label: "instance"

View File

@@ -0,0 +1,7 @@
exec bash -c 'bash -euo pipefail $WORK/command.sh 2>&1'
cmp stdout $WORK/output.txt
-- command.sh --
holos --version
-- output.txt --
0.104.1

View File

@@ -0,0 +1,226 @@
# Set $HOME because:
# - Helm uses it for temporary files
# - Git requires it for setting author name/email globally
env HOME=$WORK/.tmp
chmod 0755 $WORK/update.sh
# Configure git author for testscript execution
exec git config --global user.name 'Holos Docs'
exec git config --global user.email 'hello@holos.run'
exec git config --global init.defaultBranch main
# Remove the tutorial directory if it already exists
exec rm -rf holos-kustomize-tutorial
# Create and change to the tutorial directory, and then initialize the Holos platform
exec bash -c 'bash -euo pipefail $WORK/mkdir-and-init.sh'
cd holos-kustomize-tutorial
# Initialize git
exec bash -c 'bash -euo pipefail $WORK/git-init.sh'
# Create the component directory
exec bash -c 'bash -euo pipefail $WORK/mkdir-component.sh'
# Combine and execute the multiline httpbin component header/body/trailer files
exec cat $WORK/httpbin-component-header.sh $WORK/httpbin-component-body.cue $WORK/eof-trailer.sh
stdin stdout
exec bash -xeuo pipefail
# Combine and execute the multiline httpbin yaml header/body/trailer files
exec cat $WORK/httpbin-yaml-header.sh $WORK/httpbin-yaml-body.yaml $WORK/eof-trailer.sh
stdin stdout
exec bash -xeuo pipefail
# Combine and execute the multiline registration header/body/trailer files
exec cat $WORK/register-component-header.sh $WORK/register-component-body.cue $WORK/eof-trailer.sh
stdin stdout
exec bash -xeuo pipefail
# Render the platform, capture stdout, and use update.sh to gate whether the
# output file should be updated.
#
# NOTE: The [net] condition will test whether external network access is available
[net] exec bash -c 'bash -euo pipefail $WORK/render.sh 2>&1'
[net] stdin stdout
exec $WORK/update.sh $WORK/register-component-output.txt
# Git commit and capture output
exec bash -c 'bash -euo pipefail $WORK/git-commit-component.sh'
stdin stdout
exec $WORK/update.sh $WORK/git-commit-component-output.txt
# Export Build Plan and capture output
exec bash -c 'bash -euo pipefail $WORK/cue-export.sh'
stdin stdout
exec $WORK/update.sh $WORK/buildplan-output.cue
# Combine and execute the multiline kustomize patch header/body/trailer files
exec cat $WORK/httpbin-patch-header.sh $WORK/httpbin-patch-body.cue $WORK/eof-trailer.sh
stdin stdout
exec bash -xeuo pipefail
# Render the platform and capture output
[net] exec bash -c 'bash -euo pipefail $WORK/render.sh 2>&1'
[net] stdin stdout
exec $WORK/update.sh $WORK/kustomize-patch-render-output.txt
# Git diff and capture output
exec bash -c 'bash -euo pipefail $WORK/git-diff.sh'
stdin stdout
exec $WORK/update.sh $WORK/git.diff
# Git commit and capture output
exec bash -c 'bash -euo pipefail $WORK/git-commit-final.sh'
stdin stdout
exec $WORK/update.sh $WORK/git-commit-final-output.txt
# Clean up the tutorial directory and tmp $HOME directory
cd $WORK
exec rm -rf holos-kustomize-tutorial
exec rm -rf $HOME
-- update.sh --
#! /bin/bash
set -euo pipefail
[[ -s "$1" ]] && [[ -z "${HOLOS_UPDATE_SCRIPTS:-}" ]] && exit 0
cat > "$1"
-- mkdir-and-init.sh --
mkdir holos-kustomize-tutorial
cd holos-kustomize-tutorial
holos init platform v1alpha5
-- git-init.sh --
git init . && git add . && git commit -m initial
-- mkdir-component.sh --
mkdir -p components/httpbin
-- httpbin-component-header.sh --
cat <<EOF > components/httpbin/httpbin.cue
-- httpbin-component-body.cue --
package holos
// Produce a Kustomize BuildPlan for Holos
holos: Kustomize.BuildPlan
// https://github.com/mccutchen/go-httpbin/blob/v2.15.0/kustomize/README.md
Kustomize: #Kustomize & {
KustomizeConfig: {
// Files tells Holos to copy the file from the component path to the
// temporary directory Holos uses for BuildPlan execution.
Files: {
"httpbin.yaml": _
}
CommonLabels: {
"app.kubernetes.io/name": "httpbin"
}
// Kustomization represents a kustomization.yaml file in CUE. Holos
// marshals this field into a `kustomization.yaml` while processing a
// BuildPlan. See
// https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/
Kustomization: {
images: [{name: "mccutchen/go-httpbin"}]
// Use a hidden field to compose patches easily with a struct. Hidden
// fields are not included in exported structures.
_patches: {}
// Convert the hidden struct to a list.
patches: [for x in _patches {x}]
}
}
}
-- eof-trailer.sh --
EOF
-- httpbin-yaml-header.sh --
cat <<EOF > components/httpbin/httpbin.yaml
-- httpbin-yaml-body.yaml --
# https://github.com/mccutchen/go-httpbin/blob/v2.15.0/kustomize/resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
template:
spec:
containers:
- name: httpbin
image: mccutchen/go-httpbin
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /status/200
port: http
readinessProbe:
httpGet:
path: /status/200
port: http
resources: {}
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
spec:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
appProtocol: http
-- register-component-header.sh --
cat <<EOF > platform/httpbin.cue
-- register-component-body.cue --
package holos
Platform: Components: {
httpbin: {
name: "httpbin"
path: "components/httpbin"
}
}
-- git-commit-component.sh --
git add . && git commit -m 'add httpbin'
-- cue-export.sh --
holos cue export --expression holos --out=yaml ./components/httpbin
-- httpbin-patch-header.sh --
cat <<EOF > components/httpbin/patches.cue
-- httpbin-patch-body.cue --
package holos
import "encoding/yaml"
// Mix in a Kustomize patch to the configuration.
Kustomize: KustomizeConfig: Kustomization: _patches: {
probe: {
target: kind: "Service"
target: name: "httpbin"
patch: yaml.Marshal([{
op: "add"
path: "/metadata/annotations/prometheus.io~1probe"
value: "true"
}])
}
}
-- httpbin-component-output.txt --
rendered httpbin in 197.030208ms
rendered platform in 197.416416ms
-- render.sh --
holos render platform
-- git-diff.sh --
git diff
-- git.diff --
diff --git a/deploy/components/httpbin/httpbin.gen.yaml b/deploy/components/httpbin/httpbin.gen.yaml
index 298b9a8..a16bd1a 100644
--- a/deploy/components/httpbin/httpbin.gen.yaml
+++ b/deploy/components/httpbin/httpbin.gen.yaml
@@ -1,6 +1,8 @@
apiVersion: v1
kind: Service
metadata:
+ annotations:
+ prometheus.io/probe: "true"
labels:
app.kubernetes.io/name: httpbin
name: httpbin
-- git-commit-final.sh --
git add . && git commit -m 'annotate httpbin for prometheus probes'

View File

@@ -0,0 +1 @@
holos --version

View File

@@ -0,0 +1 @@
0.104.1

View File

@@ -0,0 +1,36 @@
kind: BuildPlan
apiVersion: v1alpha5
metadata:
name: no-name
spec:
artifacts:
- artifact: components/no-name/no-name.gen.yaml
generators:
- kind: Resources
output: resources.gen.yaml
resources: {}
- kind: File
output: httpbin.yaml
file:
source: httpbin.yaml
transformers:
- kind: Kustomize
inputs:
- resources.gen.yaml
- httpbin.yaml
output: components/no-name/no-name.gen.yaml
kustomize:
kustomization:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
labels:
- includeSelectors: false
pairs:
app.kubernetes.io/name: httpbin
patches: []
images:
- name: mccutchen/go-httpbin
resources:
- resources.gen.yaml
- httpbin.yaml
validators: []

View File

@@ -0,0 +1 @@
holos cue export --expression holos --out=yaml ./components/httpbin

View File

@@ -0,0 +1 @@
EOF

View File

@@ -0,0 +1,6 @@
[main 823e136] add httpbin
4 files changed, 113 insertions(+)
create mode 100644 components/httpbin/httpbin.cue
create mode 100644 components/httpbin/httpbin.yaml
create mode 100644 deploy/components/httpbin/httpbin.gen.yaml
create mode 100644 platform/httpbin.cue

View File

@@ -0,0 +1 @@
git add . && git commit -m 'add httpbin'

View File

@@ -0,0 +1,3 @@
[main 96f5f45] annotate httpbin for prometheus probes
2 files changed, 18 insertions(+)
create mode 100644 components/httpbin/patches.cue

View File

@@ -0,0 +1 @@
git add . && git commit -m 'annotate httpbin for prometheus probes'

View File

@@ -0,0 +1 @@
git diff

View File

@@ -0,0 +1 @@
git init . && git add . && git commit -m initial

Some files were not shown because too many files have changed in this diff Show More