mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 08:44:58 +00:00
Compare commits
92 Commits
v0.100.1
...
jeff/406-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0061e7311a | ||
|
|
d5fb9037e2 | ||
|
|
70c76daddb | ||
|
|
ad2bf5dde1 | ||
|
|
44c2fe220a | ||
|
|
fe1ae2fa80 | ||
|
|
e9d1240d63 | ||
|
|
03fa4eaaa2 | ||
|
|
e363f3a597 | ||
|
|
8b49ed93be | ||
|
|
d2be9fe278 | ||
|
|
6ec341bbb1 | ||
|
|
13a4305b78 | ||
|
|
0cfce3a823 | ||
|
|
61d7539e1c | ||
|
|
bf84724137 | ||
|
|
9f0de7555c | ||
|
|
650636f944 | ||
|
|
b28c110694 | ||
|
|
5bb3e90b38 | ||
|
|
6a60b613ff | ||
|
|
5862725bab | ||
|
|
8660826b05 | ||
|
|
449df91e33 | ||
|
|
ac59173b30 | ||
|
|
fb75e560fc | ||
|
|
69a064e3ea | ||
|
|
71b72807bb | ||
|
|
0e4ecf9d13 | ||
|
|
ec2fdadd44 | ||
|
|
38b082095f | ||
|
|
f9346ea7c0 | ||
|
|
0f7010288a | ||
|
|
386fb89cc6 | ||
|
|
c5401d6b02 | ||
|
|
f215405643 | ||
|
|
2c79982bd3 | ||
|
|
e5e4de3073 | ||
|
|
ec462f5f0b | ||
|
|
0e95a2812e | ||
|
|
54efe3e24a | ||
|
|
f693f049f4 | ||
|
|
85238710ac | ||
|
|
3ec62d272e | ||
|
|
49afb44fd4 | ||
|
|
a023f135ab | ||
|
|
c6a3a5d689 | ||
|
|
3f1eed3f06 | ||
|
|
7fb7df1441 | ||
|
|
a798111d4d | ||
|
|
3ddb823341 | ||
|
|
70d48592c4 | ||
|
|
006f08df93 | ||
|
|
39e2db5d37 | ||
|
|
ceb293fd8a | ||
|
|
188ff95015 | ||
|
|
5f658e0ba0 | ||
|
|
18b2850d3c | ||
|
|
366a7fe93d | ||
|
|
f71d6d5bd9 | ||
|
|
4529673e93 | ||
|
|
16a6447926 | ||
|
|
111a5944ff | ||
|
|
ff1446dc93 | ||
|
|
67ef990c37 | ||
|
|
6bd54ab856 | ||
|
|
89a23a10fd | ||
|
|
5a939bb6fe | ||
|
|
ee16f14e03 | ||
|
|
7530345620 | ||
|
|
47d60ef86d | ||
|
|
9e9f6efd04 | ||
|
|
fb4a043823 | ||
|
|
d718ab1910 | ||
|
|
c649db18a9 | ||
|
|
b3bddf3ee3 | ||
|
|
77836be250 | ||
|
|
4db670b854 | ||
|
|
d87c919519 | ||
|
|
2184bda2a1 | ||
|
|
a8ab4dabaa | ||
|
|
7175950ce0 | ||
|
|
e186c1be37 | ||
|
|
a998513e34 | ||
|
|
2f821ec33c | ||
|
|
4d54190f2e | ||
|
|
b466ec3457 | ||
|
|
45120797b9 | ||
|
|
e6d25bf5eb | ||
|
|
3ad6e69336 | ||
|
|
9b10e23e43 | ||
|
|
b19022d3ff |
11
.cspell.json
11
.cspell.json
@@ -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",
|
||||
|
||||
47
.github/ISSUE_TEMPLATE/bug-report.md
vendored
47
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -13,8 +13,8 @@ To ask questions, see https://github.com/holos-run/holos/discussions
|
||||
|
||||
### What version of holos are you using (`holos --version`)?
|
||||
|
||||
```shell
|
||||
holos --version
|
||||
```
|
||||
0.0.0
|
||||
```
|
||||
|
||||
### Does this issue reproduce with the latest release?
|
||||
@@ -43,23 +43,23 @@ Refer to: https://github.com/rogpeppe/go-internal/tree/master/cmd/testscript
|
||||
Steps to reproduce:
|
||||
|
||||
```shell
|
||||
brew install testscript
|
||||
testscript -v -continue <<EOF
|
||||
```
|
||||
|
||||
```shell
|
||||
testscript -v -continue example.txt
|
||||
```
|
||||
|
||||
```txt
|
||||
```txtar
|
||||
# Have: an error related to the imported Kustomize schemas.
|
||||
# Want: holos show buildplans to work.
|
||||
exec holos --version
|
||||
exec holos init platform v1alpha5 --force
|
||||
# remove the fix to trigger the bug
|
||||
rm cue.mod/pkg/sigs.k8s.io/kustomize/api/types/var.cue
|
||||
# want a BuildPlan shown
|
||||
exec holos show buildplans
|
||||
stdout 'kind: BuildPlan'
|
||||
cmp stdout buildplan.yaml
|
||||
# want this error to go away
|
||||
! stderr 'cannot convert non-concrete value string'
|
||||
-- buildplan.yaml --
|
||||
kind: BuildPlan
|
||||
-- platform/example.cue --
|
||||
package holos
|
||||
|
||||
@@ -87,6 +87,9 @@ Component: #Kustomize & {
|
||||
]
|
||||
}
|
||||
```
|
||||
```shell
|
||||
EOF
|
||||
```
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
@@ -96,29 +99,33 @@ The testscript should pass.
|
||||
|
||||
The testscript fails because of the bug.
|
||||
|
||||
```shell
|
||||
testscript -v -continue example.txt
|
||||
```
|
||||
|
||||
```txt
|
||||
# Have: an error related to the imported Kustomize schemas.
|
||||
# Want: holos show buildplans to work. (0.073s)
|
||||
# Want: holos show buildplans to work. (0.168s)
|
||||
> exec holos --version
|
||||
[stdout]
|
||||
0.100.0
|
||||
0.100.1-2-g9b10e23-dirty
|
||||
> exec holos init platform v1alpha5 --force
|
||||
# want a BuildPlan shown (0.085s)
|
||||
# remove the fix to trigger the bug (0.000s)
|
||||
> rm cue.mod/pkg/sigs.k8s.io/kustomize/api/types/var.cue
|
||||
# want a BuildPlan shown (0.091s)
|
||||
> exec holos show buildplans
|
||||
[stderr]
|
||||
could not run: holos.spec.artifacts.0.transformers.0.kustomize.kustomization.patches.0.target.name: cannot convert non-concrete value string at builder/v1alpha5/builder.go:218
|
||||
holos.spec.artifacts.0.transformers.0.kustomize.kustomization.patches.0.target.name: cannot convert non-concrete value string:
|
||||
$WORK/cue.mod/gen/sigs.k8s.io/kustomize/api/types/var_go_gen.cue:33:2
|
||||
[exit status 1]
|
||||
FAIL: example.txt:7: unexpected command failure
|
||||
> stdout 'kind: BuildPlan'
|
||||
FAIL: example.txt:8: no match for `kind: BuildPlan` found in stdout
|
||||
FAIL: <stdin>:8: unexpected command failure
|
||||
> cmp stdout buildplan.yaml
|
||||
diff stdout buildplan.yaml
|
||||
--- stdout
|
||||
+++ buildplan.yaml
|
||||
@@ -0,0 +1,1 @@
|
||||
+kind: BuildPlan
|
||||
|
||||
FAIL: <stdin>:9: stdout and buildplan.yaml differ
|
||||
# want this error to go away (0.000s)
|
||||
> ! stderr 'cannot convert non-concrete value string'
|
||||
FAIL: example.txt:10: unexpected match for `cannot convert non-concrete value string` found in stderr: cannot convert non-concrete value string
|
||||
FAIL: <stdin>:11: unexpected match for `cannot convert non-concrete value string` found in stderr: cannot convert non-concrete value string
|
||||
failed run
|
||||
```
|
||||
|
||||
143
.github/workflows/container.yaml
vendored
Normal file
143
.github/workflows/container.yaml
vendored
Normal 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 }}
|
||||
33
.github/workflows/lint.yaml
vendored
33
.github/workflows/lint.yaml
vendored
@@ -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
|
||||
|
||||
8
.github/workflows/test.yaml
vendored
8
.github/workflows/test.yaml
vendored
@@ -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
1
.gitignore
vendored
@@ -12,3 +12,4 @@ tmp/
|
||||
/holos-k3d/
|
||||
/holos-infra/
|
||||
node_modules/
|
||||
.tmp/
|
||||
|
||||
39
Dockerfile
39
Dockerfile
@@ -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"]
|
||||
|
||||
4
Makefile
4
Makefile
@@ -154,6 +154,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)
|
||||
|
||||
129
README.md
129
README.md
@@ -1,35 +1,130 @@
|
||||
## Holos - A Holistic Development Platform
|
||||
# Holos
|
||||
|
||||
<img width="50%"
|
||||
align="right"
|
||||
style="display: block; margin: 40px auto;"
|
||||
src="https://openinfrastructure.co/blog/2016/02/27/logo/logorectangle.png">
|
||||
|
||||
Building and maintaining a software development platform is a complex and time
|
||||
consuming endeavour. Organizations often dedicate a team of 3-4 who need 6-12
|
||||
months to build the platform.
|
||||
[Holos] is a configuration management tool for Kubernetes implementing the
|
||||
[rendered manifests pattern]. It handles configurations ranging from single
|
||||
resources to multi-cluster platforms across regions.
|
||||
|
||||
Holos is a tool and a reference platform to reduce the complexity and speed up
|
||||
the process of building a modern, cloud native software development platform.
|
||||
Key components:
|
||||
- Platform schemas defining component integration
|
||||
- Building blocks unifying Helm, Kustomize and Kubernetes configs with CUE
|
||||
- BuildPlan pipeline for generating, transforming and validating manifests
|
||||
|
||||
- **Accelerate new projects** - Reduce time to market and operational complexity by starting your new project on top of the Holos reference platform.
|
||||
- **Modernize existing projects** - Incrementally incorporate your existing platform services into Holos for simpler integration.
|
||||
- **Unified configuration model** - Increase safety and reduce the risk of config changes with CUE.
|
||||
- **First class Helm and Kustomize support** - Leverage and reuse your existing investment in existing configuration tools such as Helm and Kustomize.
|
||||
- **Modern Authentication and Authorization** - Holos seamlessly integrates platform identity and access management with zero-trust beyond corp style authorization policy.
|
||||
```mermaid
|
||||
---
|
||||
title: Rendering Overview
|
||||
---
|
||||
graph LR
|
||||
Platform[<a href="https://holos.run/docs/v1alpha5/api/author/#Platform">Platform</a>]
|
||||
Component[<a href="https://holos.run/docs/v1alpha5/api/author/#ComponentConfig">Components</a>]
|
||||
|
||||
## Quick Installation
|
||||
Helm[<a href="https://holos.run/docs/v1alpha5/api/author/#Helm">Helm</a>]
|
||||
Kustomize[<a href="https://holos.run/docs/v1alpha5/api/author/#Kustomize">Kustomize</a>]
|
||||
Kubernetes[<a href="https://holos.run/docs/v1alpha5/api/author/#Kubernetes">Kubernetes</a>]
|
||||
|
||||
```console
|
||||
go install github.com/holos-run/holos/cmd/holos@latest
|
||||
BuildPlan[<a href="https://holos.run/docs/v1alpha5/api/core/#BuildPlan">BuildPlan</a>]
|
||||
|
||||
ResourcesArtifact[<a href="https://holos.run/docs/v1alpha5/api/core/#Artifact">Resources<br/>Artifact</a>]
|
||||
GitOpsArtifact[<a href="https://holos.run/docs/v1alpha5/api/core/#Artifact">GitOps<br/>Artifact</a>]
|
||||
|
||||
Generators[<a href="https://holos.run/docs/v1alpha5/api/core/#Generator">Generators</a>]
|
||||
Transformers[<a href="https://holos.run/docs/v1alpha5/api/core/#Transformer">Transformers</a>]
|
||||
Validators[<a href="https://holos.run/docs/v1alpha5/api/core/#Validator">Validators</a>]
|
||||
Files[Manifest<br/>Files]
|
||||
|
||||
Platform --> Component
|
||||
Component --> Helm --> BuildPlan
|
||||
Component --> Kubernetes --> BuildPlan
|
||||
Component --> Kustomize --> BuildPlan
|
||||
|
||||
BuildPlan --> ResourcesArtifact --> Generators
|
||||
BuildPlan --> GitOpsArtifact --> Generators
|
||||
|
||||
Generators --> Transformers --> Validators --> Files
|
||||
```
|
||||
|
||||
## Docs and Support
|
||||
## Setup
|
||||
|
||||
The documentation for developing and using Holos is available at: https://holos.run
|
||||
```shell
|
||||
brew install holos-run/tap/holos
|
||||
```
|
||||
|
||||
For discussion and support, [open a discussion](https://github.com/orgs/holos-run/discussions/new/choose).
|
||||
Refer to [setup] for other installation methods and dependencies.
|
||||
|
||||
## Example
|
||||
|
||||
See our [tutorial] for a complete hello world example.
|
||||
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
|
||||
holos: Component.BuildPlan
|
||||
|
||||
Component: #Helm & {
|
||||
Name: "podinfo"
|
||||
Chart: {
|
||||
version: "6.6.2"
|
||||
repository: {
|
||||
name: "podinfo"
|
||||
url: "https://stefanprodan.github.io/podinfo"
|
||||
}
|
||||
}
|
||||
Values: ui: {
|
||||
message: string | *"Hello World" @tag(message, type=string)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Organizational Role
|
||||
|
||||
Platform engineers use Holos to generate Kubernetes manifests, both locally and
|
||||
in CI pipelines. The manifests are committed to version control and deployed via
|
||||
GitOps tools like ArgoCD or Flux.
|
||||
|
||||
Holos integrates seamlessly with existing Helm charts, Kustomize bases, and
|
||||
other version-controlled configurations.
|
||||
|
||||
## Advantages of Holos
|
||||
|
||||
### Safe
|
||||
|
||||
Holos leverages [CUE] for strong typing and validation of configuration data,
|
||||
ensuring consistent output from Helm and other tools.
|
||||
|
||||
### Consistent
|
||||
|
||||
A unified pipeline processes all configurations - whether from CUE, Helm, or
|
||||
Kustomize - through the same well-defined stages.
|
||||
|
||||
### Flexible
|
||||
|
||||
Composable building blocks for generation, transformation, validation and
|
||||
integration let teams assemble workflows that match their needs.
|
||||
|
||||
The core is intentionally unopinionated about platform configuration patterns.
|
||||
Common needs like environments and clusters are provided as customizable
|
||||
[topics] recipes rather than enforced structures.
|
||||
|
||||
## Getting Help
|
||||
|
||||
Get support through our [Discord] channel or [GitHub discussions]. Configuration
|
||||
challenges arise at all experience levels - we welcome your questions and are
|
||||
here to help.
|
||||
|
||||
## License
|
||||
|
||||
Holos is licensed under Apache 2.0 as found in the [LICENSE file](LICENSE).
|
||||
|
||||
[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/
|
||||
[tutorial]: https://holos.run/docs/overview/
|
||||
[setup]: https://holos.run/docs/setup/
|
||||
[topics]: https://holos.run/docs/topics/
|
||||
|
||||
@@ -65,8 +65,10 @@ type ComponentConfig struct {
|
||||
|
||||
// Resources represents kubernetes resources mixed into the rendered manifest.
|
||||
Resources core.Resources
|
||||
// KustomizeConfig represents the configuration kustomize.
|
||||
// KustomizeConfig represents the kustomize configuration.
|
||||
KustomizeConfig KustomizeConfig
|
||||
// Validators represent checks that must pass for output to be written.
|
||||
Validators map[NameLabel]core.Validator
|
||||
// Artifacts represents additional artifacts to mix in. Useful for adding
|
||||
// GitOps resources. Each Artifact is unified without modification into the
|
||||
// BuildPlan.
|
||||
@@ -82,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.
|
||||
|
||||
@@ -40,14 +40,6 @@ type BuildPlanSpec struct {
|
||||
Disabled bool `json:"disabled,omitempty" yaml:"disabled,omitempty"`
|
||||
}
|
||||
|
||||
// BuildPlanSource reflects the origin of a [BuildPlan]. Useful to save a build
|
||||
// plan to a file, then re-generate it without needing to process a [Platform]
|
||||
// component collection.
|
||||
type BuildPlanSource struct {
|
||||
// Component reflects the component that produced the build plan.
|
||||
Component Component `json:"component,omitempty" yaml:"component,omitempty"`
|
||||
}
|
||||
|
||||
// Artifact represents one fully rendered manifest produced by a [Transformer]
|
||||
// sequence, which transforms a [Generator] collection. A [BuildPlan] produces
|
||||
// an [Artifact] collection.
|
||||
@@ -72,6 +64,7 @@ type Artifact struct {
|
||||
Artifact FilePath `json:"artifact,omitempty" yaml:"artifact,omitempty"`
|
||||
Generators []Generator `json:"generators,omitempty" yaml:"generators,omitempty"`
|
||||
Transformers []Transformer `json:"transformers,omitempty" yaml:"transformers,omitempty"`
|
||||
Validators []Validator `json:"validators,omitempty" yaml:"validators,omitempty"`
|
||||
Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"`
|
||||
}
|
||||
|
||||
@@ -125,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
|
||||
@@ -137,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
|
||||
|
||||
@@ -153,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]
|
||||
@@ -206,15 +231,37 @@ type Kustomize struct {
|
||||
// is expected to happen in CUE against the kubectl version the user prefers.
|
||||
type Kustomization map[string]any
|
||||
|
||||
// FileContent represents file contents.
|
||||
type FileContent string
|
||||
|
||||
// FileContentMap represents a mapping of file paths to file contents.
|
||||
type FileContentMap map[FilePath]FileContent
|
||||
|
||||
// FilePath represents a file path.
|
||||
type FilePath string
|
||||
|
||||
// FileContent represents file contents.
|
||||
type FileContent string
|
||||
|
||||
// Validator validates files. Useful to validate an [Artifact] prior to writing
|
||||
// it out to the final destination. Holos may execute validators concurrently.
|
||||
// See the [validators] tutorial for an end to end example.
|
||||
//
|
||||
// [validators]: https://holos.run/docs/v1alpha5/tutorial/validators/
|
||||
type Validator struct {
|
||||
// Kind represents the kind of transformer. Must be Kustomize, or Join.
|
||||
Kind string `json:"kind" yaml:"kind" cue:"\"Command\""`
|
||||
// Inputs represents the files to validate. Usually the final Artifact.
|
||||
Inputs []FilePath `json:"inputs" yaml:"inputs"`
|
||||
// Command represents a validation command. Ignored unless kind is Command.
|
||||
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.
|
||||
type Command struct {
|
||||
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
|
||||
}
|
||||
|
||||
// InternalLabel is an arbitrary unique identifier internal to holos itself.
|
||||
// The holos cli is expected to never write a InternalLabel value to rendered
|
||||
// output files, therefore use a InternalLabel when the identifier must be
|
||||
@@ -231,7 +278,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"`
|
||||
}
|
||||
@@ -271,6 +318,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"`
|
||||
@@ -284,6 +335,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
63
cmd/cmd.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()())
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ 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(),
|
||||
"holos": cmd.MakeMain(),
|
||||
"cue": cue.Main,
|
||||
}))
|
||||
}
|
||||
|
||||
12
cmd/holos/tests/cli/cue-vet.txt
Normal file
12
cmd/holos/tests/cli/cue-vet.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
# https://github.com/holos-run/holos/issues/358
|
||||
# holos cue vet should fail verifications with exit code 1
|
||||
! exec holos cue vet ./policy --path strings.ToLower(kind) ./data/secret.yaml
|
||||
# holos cue vet should report validation errors to stderr
|
||||
stderr 'Forbidden. Use an ExternalSecret instead.'
|
||||
|
||||
-- data/secret.yaml --
|
||||
kind: Secret
|
||||
-- policy/validators.cue --
|
||||
package policy
|
||||
|
||||
secret: kind: "Forbidden. Use an ExternalSecret instead."
|
||||
@@ -7,11 +7,11 @@ cd $WORK
|
||||
exec holos generate platform v1alpha5 --force
|
||||
|
||||
# Platforms are empty by default.
|
||||
exec holos render platform ./platform
|
||||
exec holos render platform
|
||||
stderr -count=1 '^rendered platform'
|
||||
|
||||
# Holos uses CUE to build a platform specification.
|
||||
exec cue export --expression holos --out=yaml ./platform
|
||||
exec holos show platform
|
||||
cmp stdout want/1.platform_spec.yaml
|
||||
|
||||
# Define the host and port in projects/blackbox.schema.cue
|
||||
@@ -22,7 +22,7 @@ mv projects/platform/components/prometheus/prometheus.cue.disabled projects/plat
|
||||
mv platform/prometheus.cue.disabled platform/prometheus.cue
|
||||
|
||||
# Render the platform to render the prometheus chart.
|
||||
exec holos render platform ./platform
|
||||
exec holos render platform
|
||||
stderr -count=1 '^rendered prometheus'
|
||||
stderr -count=1 '^rendered platform'
|
||||
cmp deploy/components/prometheus/prometheus.gen.yaml want/1.prometheus.gen.yaml
|
||||
@@ -73,8 +73,8 @@ core.#BuildPlan & {
|
||||
metadata: name: _Tags.component.name
|
||||
}
|
||||
-- want/1.platform_spec.yaml --
|
||||
kind: Platform
|
||||
apiVersion: v1alpha5
|
||||
kind: Platform
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,9 +106,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
---
|
||||
@@ -135,9 +132,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
---
|
||||
@@ -164,9 +158,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
---
|
||||
@@ -193,9 +184,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
-- want/buildplans.1.yaml --
|
||||
@@ -222,9 +210,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
-- want/buildplans.2.yaml --
|
||||
@@ -251,9 +236,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
-- want/buildplans.3.yaml --
|
||||
@@ -280,9 +262,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
---
|
||||
@@ -309,9 +288,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
---
|
||||
@@ -338,9 +314,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
-- want/buildplans.4.yaml --
|
||||
@@ -367,9 +340,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
---
|
||||
@@ -396,9 +366,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
---
|
||||
@@ -425,9 +392,6 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
---
|
||||
@@ -454,8 +418,5 @@ spec:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
exec holos init platform v1alpha5 --force
|
||||
# want a buildplan shown
|
||||
exec holos show buildplans
|
||||
stdout 'kind: BuildPlan'
|
||||
cmp stdout buildplan.yaml
|
||||
# want this error to go away
|
||||
! stderr 'cannot convert non-concrete value string'
|
||||
-- platform/example.cue --
|
||||
@@ -32,3 +32,33 @@ Component: #Kustomize & {
|
||||
},
|
||||
]
|
||||
}
|
||||
-- buildplan.yaml --
|
||||
kind: BuildPlan
|
||||
apiVersion: v1alpha5
|
||||
metadata:
|
||||
name: example
|
||||
spec:
|
||||
artifacts:
|
||||
- artifact: components/example/example.gen.yaml
|
||||
generators:
|
||||
- kind: Resources
|
||||
output: resources.gen.yaml
|
||||
transformers:
|
||||
- kind: Kustomize
|
||||
inputs:
|
||||
- resources.gen.yaml
|
||||
output: components/example/example.gen.yaml
|
||||
kustomize:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
patches:
|
||||
- patch: |
|
||||
- op: add
|
||||
path: /metadata/annotations/example
|
||||
value: example-value
|
||||
target:
|
||||
kind: CustomResourceDefinition
|
||||
name: ""
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
|
||||
50
cmd/holos/tests/v1alpha5/issues/issue366-cue-build-tags.txt
Normal file
50
cmd/holos/tests/v1alpha5/issues/issue366-cue-build-tags.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
# https://github.com/holos-run/holos/issues/366
|
||||
# Build tags conditionally include CUE files.
|
||||
env HOME=$WORK
|
||||
|
||||
exec holos init platform v1alpha5 --force
|
||||
exec holos show platform
|
||||
cmp stdout want/empty.yaml
|
||||
|
||||
exec holos show platform -t foo
|
||||
cmp stdout want/foo.yaml
|
||||
|
||||
-- platform/empty.cue --
|
||||
@if(foo)
|
||||
package holos
|
||||
|
||||
Platform: Components: foo: _
|
||||
-- platform/metadata.cue --
|
||||
package holos
|
||||
|
||||
Platform: Components: [NAME=string]: {
|
||||
name: NAME
|
||||
path: "components/empty"
|
||||
labels: "app.holos.run/name": NAME
|
||||
annotations: "app.holos.run/description": "\(NAME) empty test case"
|
||||
}
|
||||
-- components/empty/empty.cue --
|
||||
package holos
|
||||
|
||||
Component: #Kubernetes & {}
|
||||
holos: Component.BuildPlan
|
||||
-- want/empty.yaml --
|
||||
apiVersion: v1alpha5
|
||||
kind: Platform
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
components: []
|
||||
-- want/foo.yaml --
|
||||
apiVersion: v1alpha5
|
||||
kind: Platform
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
components:
|
||||
- annotations:
|
||||
app.holos.run/description: foo empty test case
|
||||
labels:
|
||||
app.holos.run/name: foo
|
||||
name: foo
|
||||
path: components/empty
|
||||
@@ -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
|
||||
|
||||
@@ -31,6 +31,7 @@ spec:
|
||||
- kind: Resources
|
||||
output: resources.gen.yaml
|
||||
resources: {}
|
||||
validators: []
|
||||
transformers:
|
||||
- kind: Kustomize
|
||||
inputs:
|
||||
@@ -38,9 +39,6 @@ spec:
|
||||
output: components/no-name/no-name.gen.yaml
|
||||
kustomize:
|
||||
kustomization:
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- resources.gen.yaml
|
||||
kind: Kustomization
|
||||
|
||||
37
cmd/holos/tests/v1alpha5/schemas/validators.txt
Normal file
37
cmd/holos/tests/v1alpha5/schemas/validators.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
# https://github.com/holos-run/holos/issues/357
|
||||
exec holos init platform v1alpha5 --force
|
||||
! exec holos render platform
|
||||
stderr 'secret.kind: conflicting values "Forbidden. Use an ExternalSecret instead." and "Secret"'
|
||||
|
||||
-- validators.cue --
|
||||
package holos
|
||||
|
||||
import "github.com/holos-run/holos/api/author/v1alpha5:author"
|
||||
|
||||
#ComponentConfig: author.#ComponentConfig & {
|
||||
Validators: cue: {
|
||||
kind: "Command"
|
||||
command: args: ["holos", "cue", "vet", "./policy", "--path", "strings.ToLower(kind)"]
|
||||
}
|
||||
}
|
||||
-- policy/validations.cue --
|
||||
package validations
|
||||
|
||||
secret: kind: "Forbidden. Use an ExternalSecret instead."
|
||||
-- platform/example.cue --
|
||||
package holos
|
||||
|
||||
Platform: Components: example: {
|
||||
name: "example"
|
||||
path: "components/example"
|
||||
}
|
||||
-- components/example/secret.cue --
|
||||
package holos
|
||||
|
||||
holos: Component.BuildPlan
|
||||
|
||||
Component: #Kubernetes & {
|
||||
Resources: Secret: test: {
|
||||
metadata: name: "test"
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,10 @@ type ComponentConfig struct {
|
||||
|
||||
// Resources represents kubernetes resources mixed into the rendered manifest.
|
||||
Resources core.Resources
|
||||
// KustomizeConfig represents the configuration kustomize.
|
||||
// KustomizeConfig represents the kustomize configuration.
|
||||
KustomizeConfig KustomizeConfig
|
||||
// Validators represent checks that must pass for output to be written.
|
||||
Validators map[NameLabel]core.Validator
|
||||
// Artifacts represents additional artifacts to mix in. Useful for adding
|
||||
// GitOps resources. Each Artifact is unified without modification into the
|
||||
// BuildPlan.
|
||||
@@ -84,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.
|
||||
|
||||
@@ -15,17 +15,21 @@ 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 BuildPlanSource](<#BuildPlanSource>)
|
||||
- [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>)
|
||||
@@ -38,6 +42,8 @@ Package core contains schemas for a [Platform](<#Platform>) and [BuildPlan](<#Bu
|
||||
- [type Resource](<#Resource>)
|
||||
- [type Resources](<#Resources>)
|
||||
- [type Transformer](<#Transformer>)
|
||||
- [type Validator](<#Validator>)
|
||||
- [type ValueFile](<#ValueFile>)
|
||||
- [type Values](<#Values>)
|
||||
|
||||
|
||||
@@ -59,10 +65,35 @@ type Artifact struct {
|
||||
Artifact FilePath `json:"artifact,omitempty" yaml:"artifact,omitempty"`
|
||||
Generators []Generator `json:"generators,omitempty" yaml:"generators,omitempty"`
|
||||
Transformers []Transformer `json:"transformers,omitempty" yaml:"transformers,omitempty"`
|
||||
Validators []Validator `json:"validators,omitempty" yaml:"validators,omitempty"`
|
||||
Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
<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}
|
||||
|
||||
@@ -85,18 +116,6 @@ type BuildPlan struct {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="BuildPlanSource"></a>
|
||||
## type BuildPlanSource {#BuildPlanSource}
|
||||
|
||||
BuildPlanSource reflects the origin of a [BuildPlan](<#BuildPlan>). Useful to save a build plan to a file, then re\-generate it without needing to process a [Platform](<#Platform>) component collection.
|
||||
|
||||
```go
|
||||
type BuildPlanSource struct {
|
||||
// Component reflects the component that produced the build plan.
|
||||
Component Component `json:"component,omitempty" yaml:"component,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="BuildPlanSpec"></a>
|
||||
## type BuildPlanSpec {#BuildPlanSpec}
|
||||
|
||||
@@ -129,6 +148,17 @@ 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.
|
||||
|
||||
```go
|
||||
type Command struct {
|
||||
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="Component"></a>
|
||||
## type Component {#Component}
|
||||
|
||||
@@ -142,6 +172,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"`
|
||||
@@ -155,11 +189,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}
|
||||
|
||||
@@ -239,8 +284,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
|
||||
@@ -252,6 +301,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}
|
||||
|
||||
@@ -316,7 +381,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"`
|
||||
}
|
||||
@@ -364,10 +429,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"`
|
||||
}
|
||||
```
|
||||
|
||||
@@ -414,6 +482,39 @@ type Transformer struct {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="Validator"></a>
|
||||
## type Validator {#Validator}
|
||||
|
||||
Validator validates files. Useful to validate an [Artifact](<#Artifact>) prior to writing it out to the final destination. Holos may execute validators concurrently. See the [validators](<https://holos.run/docs/v1alpha5/tutorial/validators/>) tutorial for an end to end example.
|
||||
|
||||
```go
|
||||
type Validator struct {
|
||||
// Kind represents the kind of transformer. Must be Kustomize, or Join.
|
||||
Kind string `json:"kind" yaml:"kind" cue:"\"Command\""`
|
||||
// Inputs represents the files to validate. Usually the final Artifact.
|
||||
Inputs []FilePath `json:"inputs" yaml:"inputs"`
|
||||
// Command represents a validation command. Ignored unless kind is Command.
|
||||
Command Command `json:"command,omitempty" yaml:"command,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
<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}
|
||||
|
||||
|
||||
57
doc/md/topics/comparison.mdx
Normal file
57
doc/md/topics/comparison.mdx
Normal 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
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
description: Re-use components by passing in parameters.
|
||||
slug: component-parameters
|
||||
sidebar_position: 400
|
||||
---
|
||||
|
||||
# Component Parameters
|
||||
|
||||
Key points:
|
||||
|
||||
1. Components can be reused.
|
||||
2. The Platform spec can pass user defined parameters to a component.
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
description: Organize clusters into fleets.
|
||||
slug: fleets
|
||||
sidebar_position: 300
|
||||
---
|
||||
|
||||
# Fleets
|
||||
|
||||
Key points:
|
||||
|
||||
1. Workload fleet.
|
||||
2. Management fleet.
|
||||
@@ -177,7 +177,7 @@ source:
|
||||
<Tabs groupId="E150C802-7162-4FBF-82A7-77D9ADAEE847">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform ./platform
|
||||
holos render platform
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
@@ -218,7 +218,6 @@ spec:
|
||||
[CUE Module]: https://cuelang.org/docs/reference/modules/
|
||||
[CUE Tags]: https://cuelang.org/docs/howto/inject-value-into-evaluation-using-tag-attribute/
|
||||
[Application]: https://argo-cd.readthedocs.io/en/stable/user-guide/application-specification/
|
||||
[Component Parameters]: ../component-parameters.mdx
|
||||
[Platform]: ../../api/author.md#Platform
|
||||
[ComponentConfig]: ../../api/author.md#ComponentConfig
|
||||
[Artifact]: ../../api/core.md#Artifact
|
||||
|
||||
218
doc/md/topics/gitops/flux-kustomization.mdx
Normal file
218
doc/md/topics/gitops/flux-kustomization.mdx
Normal 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
20
doc/md/topics/kargo.mdx
Normal 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
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
description: Management Cluster
|
||||
slug: management-cluster
|
||||
sidebar_position: 200
|
||||
---
|
||||
|
||||
# Management Cluster
|
||||
|
||||
Key points:
|
||||
|
||||
1. Namespaces
|
||||
2. Certificates
|
||||
3. Secrets
|
||||
4. CronJobs
|
||||
5. GKE autopilot
|
||||
65
doc/md/topics/oci-helm-charts.mdx
Normal file
65
doc/md/topics/oci-helm-charts.mdx
Normal 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
|
||||
```
|
||||
183
doc/md/topics/private-helm.mdx
Normal file
183
doc/md/topics/private-helm.mdx
Normal 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
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
description: Secrets Management
|
||||
slug: secrets-management
|
||||
sidebar_position: 150
|
||||
---
|
||||
|
||||
# Secrets Management
|
||||
|
||||
Key points:
|
||||
|
||||
1. Namespaces
|
||||
2. ExternalSecrets
|
||||
@@ -39,11 +39,12 @@ mkdir holos-multiple-clusters && cd holos-multiple-clusters
|
||||
holos init platform v1alpha5
|
||||
```
|
||||
|
||||
### Example Component
|
||||
### Using an example Component
|
||||
|
||||
<CommonComponent />
|
||||
|
||||
We'll integrate the component with the platform after we define clusters.
|
||||
We'll integrate the component with the platform after we define the
|
||||
configuration structures.
|
||||
|
||||
## Defining Clusters
|
||||
|
||||
@@ -320,7 +321,7 @@ Render the platform to configure `podinfo` on each cluster.
|
||||
<Tabs groupId="34A2D80B-0E86-4142-B65B-7DF70C47E1D2">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform ./platform
|
||||
holos render platform
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
|
||||
@@ -7,23 +7,515 @@ sidebar_position: 130
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CommonComponent from '../../common/example-component.mdx';
|
||||
|
||||
# Environments
|
||||
|
||||
## Overview
|
||||
|
||||
This topic covers how to model environments in Holos. We'll define schemas for
|
||||
`#Environment` and `#Environments` to represent one environment and a
|
||||
collection. The `Environments: #Environments` struct maps environment names to
|
||||
configurations.
|
||||
|
||||
:::note
|
||||
This approach unifies the component definition with the overall platform
|
||||
configuration, creating a tight coupling between the two.
|
||||
:::
|
||||
|
||||
This tight coupling is appropriate when you're configuring your own platform.
|
||||
For example:
|
||||
|
||||
1. When you're integrating third party software into your own platform.
|
||||
2. When you're configuring first party in-house software into your own platform.
|
||||
|
||||
This approach is not well suited to writing a component to share outside of your
|
||||
own organization, which we can think of as configuring someone else's platform.
|
||||
|
||||
## The Code
|
||||
|
||||
### Generating the structure
|
||||
|
||||
Use `holos init platform` to generate a minimal platform structure:
|
||||
|
||||
```shell
|
||||
mkdir holos-environments-tutorial && cd holos-environments-tutorial
|
||||
holos init platform v1alpha5
|
||||
```
|
||||
|
||||
### Using an example Component
|
||||
|
||||
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.
|
||||
|
||||
## Defining Environments
|
||||
|
||||
### Defining one Environment
|
||||
We'll define an `#Environment` schema `#Environments` collection. We'll use
|
||||
these schemas to define an `Environments` struct of concrete configuration
|
||||
values.
|
||||
|
||||
### Defining a collection of Environments
|
||||
### Assumptions
|
||||
|
||||
## Rendering manifests
|
||||
There are two tiers of environments, prod and nonprod. Prod environments
|
||||
organized along broad jurisdictions, for example US and EU. Nonprod
|
||||
environments are organized by purpose, dev, test, and stage.
|
||||
|
||||
## Reviewing the Manifests
|
||||
### Prototyping the data
|
||||
|
||||
Before we define the schema, let's prototype the data structure we want to work
|
||||
with from the perspective of each component.
|
||||
|
||||
Let's imagine we're configuring `podinfo` to comply with regulations. When
|
||||
podinfo is deployed to production in the EU, we'll configure opt-in behavior.
|
||||
In the US we'll configure opt-out behavior.
|
||||
|
||||
We'll pass the environment name as a component parameter. The component
|
||||
definition can then look up the jurisdiction to determine the appropriate
|
||||
configuration values.
|
||||
|
||||
```shell
|
||||
holos cue export --out=yaml --expression Environments
|
||||
```
|
||||
|
||||
```yaml showLineNumbers
|
||||
prod-pdx:
|
||||
name: prod-pdx
|
||||
tier: prod
|
||||
jurisdiction: us
|
||||
state: oregon
|
||||
prod-cmh:
|
||||
name: prod-cmh
|
||||
tier: prod
|
||||
jurisdiction: us
|
||||
state: ohio
|
||||
prod-ams:
|
||||
name: prod-ams
|
||||
tier: prod
|
||||
jurisdiction: eu
|
||||
state: netherlands
|
||||
dev:
|
||||
name: dev
|
||||
tier: nonprod
|
||||
jurisdiction: us
|
||||
state: oregon
|
||||
test:
|
||||
name: test
|
||||
tier: nonprod
|
||||
jurisdiction: us
|
||||
state: oregon
|
||||
stage:
|
||||
name: stage
|
||||
tier: nonprod
|
||||
jurisdiction: us
|
||||
state: oregon
|
||||
```
|
||||
|
||||
### Defining the schema
|
||||
|
||||
Given the example structure, we can write a schema to define and validate the
|
||||
data.
|
||||
|
||||
```shell
|
||||
cat <<EOF > environments.schema.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
|
||||
#Environment: {
|
||||
name: string
|
||||
tier: "prod" | "nonprod"
|
||||
jurisdiction: "us" | "eu" | "uk" | "global"
|
||||
state: "oregon" | "ohio" | "germany" | "netherlands" | "england" | "global"
|
||||
|
||||
// Prod environment names must be prefixed with prod for clarity.
|
||||
if tier == "prod" {
|
||||
name: "prod" | =~"^prod-"
|
||||
}
|
||||
}
|
||||
|
||||
#Environments: {
|
||||
[NAME=string]: #Environment & {
|
||||
name: NAME
|
||||
}
|
||||
}
|
||||
```
|
||||
```shell
|
||||
EOF
|
||||
```
|
||||
|
||||
### Adding configuration
|
||||
|
||||
With a schema defined, we can fill in the concrete values.
|
||||
|
||||
```shell
|
||||
cat <<EOF > environments.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
|
||||
// Injected from Platform.spec.components.parameters.EnvironmentName
|
||||
EnvironmentName: string @tag(EnvironmentName)
|
||||
|
||||
Environments: #Environments & {
|
||||
"prod-pdx": {
|
||||
tier: "prod"
|
||||
jurisdiction: "us"
|
||||
state: "oregon"
|
||||
}
|
||||
"prod-cmh": {
|
||||
tier: "prod"
|
||||
jurisdiction: "us"
|
||||
state: "ohio"
|
||||
}
|
||||
"prod-ams": {
|
||||
tier: "prod"
|
||||
jurisdiction: "eu"
|
||||
state: "netherlands"
|
||||
}
|
||||
// Nonprod environments are colocated together.
|
||||
_nonprod: {
|
||||
tier: "nonprod"
|
||||
jurisdiction: "us"
|
||||
state: "oregon"
|
||||
}
|
||||
dev: _nonprod
|
||||
test: _nonprod
|
||||
stage: _nonprod
|
||||
}
|
||||
```
|
||||
```shell
|
||||
EOF
|
||||
```
|
||||
|
||||
### Inspecting the configuration
|
||||
|
||||
Inspect the `Environments` data structure to verify the schema and concrete
|
||||
values are what we want.
|
||||
|
||||
<Tabs groupId="FF820F5A-A85F-464D-B299-39CAAFFCE5C6">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos cue export --out=yaml --expression Environments
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```yaml showLineNumbers
|
||||
prod-pdx:
|
||||
name: prod-pdx
|
||||
tier: prod
|
||||
jurisdiction: us
|
||||
state: oregon
|
||||
prod-cmh:
|
||||
name: prod-cmh
|
||||
tier: prod
|
||||
jurisdiction: us
|
||||
state: ohio
|
||||
prod-ams:
|
||||
name: prod-ams
|
||||
tier: prod
|
||||
jurisdiction: eu
|
||||
state: netherlands
|
||||
dev:
|
||||
name: dev
|
||||
tier: nonprod
|
||||
jurisdiction: us
|
||||
state: oregon
|
||||
test:
|
||||
name: test
|
||||
tier: nonprod
|
||||
jurisdiction: us
|
||||
state: oregon
|
||||
stage:
|
||||
name: stage
|
||||
tier: nonprod
|
||||
jurisdiction: us
|
||||
state: oregon
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
This looks like our prototype, we're confident we can iterate over each
|
||||
environment and get a handle on the configuration values we need.
|
||||
|
||||
## Integrating components
|
||||
|
||||
The `Environments` data structure unlocks the capability to look up concrete
|
||||
values specific to a named environment. We'll use this capability to configure
|
||||
the `podinfo` component in compliance with the regulations of the jurisdiction.
|
||||
|
||||
### Configuring the environment
|
||||
|
||||
Inject the environment name when we integrate `podinfo` with the platform.
|
||||
|
||||
```shell
|
||||
cat <<EOF > platform/podinfo.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
|
||||
Platform: Components: {
|
||||
podinfoPDX: ProdPodinfo & {_city: "pdx"}
|
||||
podinfoCMH: ProdPodinfo & {_city: "cmh"}
|
||||
podinfoAMS: ProdPodinfo & {_city: "ams"}
|
||||
podinfoDEV: {
|
||||
name: "podinfo-dev"
|
||||
path: "components/podinfo"
|
||||
labels: "app.holos.run/component": "podinfo"
|
||||
parameters: EnvironmentName: "dev"
|
||||
}
|
||||
}
|
||||
|
||||
let ProdPodinfo = {
|
||||
_city: string
|
||||
name: "podinfo-\(_city)"
|
||||
path: "components/podinfo"
|
||||
labels: "app.holos.run/component": "podinfo"
|
||||
labels: "app.holos.run/tier": "prod"
|
||||
labels: "app.holos.run/city": _city
|
||||
parameters: EnvironmentName: "prod-\(_city)"
|
||||
}
|
||||
```
|
||||
```
|
||||
EOF
|
||||
```
|
||||
|
||||
### Using the environment
|
||||
|
||||
Now we can configure `podinfo` based on the jurisdiction of the environment.
|
||||
|
||||
```shell
|
||||
cat <<EOF > components/podinfo/cookie-consent.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
|
||||
// Schema definition for our configuration.
|
||||
#Values: {
|
||||
ui: enableCookieConsent: *true | false
|
||||
ui: message: string
|
||||
}
|
||||
|
||||
// Map jurisdiction to helm values
|
||||
JurisdictionValues: {
|
||||
// Enable cookie consent by default in any jurisdiction.
|
||||
[_]: #Values
|
||||
// Disable in the US.
|
||||
us: ui: enableCookieConsent: false
|
||||
eu: ui: enableCookieConsent: true
|
||||
}
|
||||
|
||||
// Look up the configuration values associated with the environment name.
|
||||
Component: Values: JurisdictionValues[Environments[EnvironmentName].jurisdiction]
|
||||
```
|
||||
```shell
|
||||
EOF
|
||||
```
|
||||
|
||||
### Inspecting the BuildPlans
|
||||
|
||||
With the above configuration, we can inspect the buildplans for this component.
|
||||
The prod environment in Amsterdam has cookie consent enabled on line 26.
|
||||
|
||||
<Tabs groupId="6EC991F3-F78C-43F1-8A6D-E68D8BDAF58B">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos show buildplans --selector app.holos.run/city=ams
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```yaml showLineNumbers
|
||||
kind: BuildPlan
|
||||
apiVersion: v1alpha5
|
||||
metadata:
|
||||
name: podinfo-ams
|
||||
labels:
|
||||
app.holos.run/city: ams
|
||||
app.holos.run/component: podinfo
|
||||
app.holos.run/name: podinfo-ams
|
||||
app.holos.run/tier: prod
|
||||
spec:
|
||||
artifacts:
|
||||
- artifact: components/podinfo-ams/podinfo-ams.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:
|
||||
# highlight-next-line
|
||||
enableCookieConsent: true
|
||||
message: Hello World
|
||||
- kind: Resources
|
||||
output: resources.gen.yaml
|
||||
transformers:
|
||||
- kind: Kustomize
|
||||
inputs:
|
||||
- helm.gen.yaml
|
||||
- resources.gen.yaml
|
||||
output: components/podinfo-ams/podinfo-ams.gen.yaml
|
||||
kustomize:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- helm.gen.yaml
|
||||
- resources.gen.yaml
|
||||
- artifact: gitops/podinfo-ams.application.gen.yaml
|
||||
generators:
|
||||
- kind: Resources
|
||||
output: gitops/podinfo-ams.application.gen.yaml
|
||||
resources:
|
||||
Application:
|
||||
podinfo-ams:
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: podinfo-ams
|
||||
namespace: argocd
|
||||
spec:
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
project: default
|
||||
source:
|
||||
path: deploy/components/podinfo-ams
|
||||
repoURL: https://github.com/brenix/holos-demo.git
|
||||
targetRevision: main
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
In Portland cookie consent is disabled.
|
||||
|
||||
<Tabs groupId="3438335B-1FFC-4838-B8DE-C54B8346CDB4">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos show buildplans --selector app.holos.run/city=pdx
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```yaml showLineNumbers
|
||||
kind: BuildPlan
|
||||
apiVersion: v1alpha5
|
||||
metadata:
|
||||
name: podinfo-pdx
|
||||
labels:
|
||||
app.holos.run/city: pdx
|
||||
app.holos.run/component: podinfo
|
||||
app.holos.run/name: podinfo-pdx
|
||||
app.holos.run/tier: prod
|
||||
spec:
|
||||
artifacts:
|
||||
- artifact: components/podinfo-pdx/podinfo-pdx.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:
|
||||
# highlight-next-line
|
||||
enableCookieConsent: false
|
||||
message: Hello World
|
||||
- kind: Resources
|
||||
output: resources.gen.yaml
|
||||
transformers:
|
||||
- kind: Kustomize
|
||||
inputs:
|
||||
- helm.gen.yaml
|
||||
- resources.gen.yaml
|
||||
output: components/podinfo-pdx/podinfo-pdx.gen.yaml
|
||||
kustomize:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
labels:
|
||||
- includeSelectors: false
|
||||
pairs: {}
|
||||
resources:
|
||||
- helm.gen.yaml
|
||||
- resources.gen.yaml
|
||||
- artifact: gitops/podinfo-pdx.application.gen.yaml
|
||||
generators:
|
||||
- kind: Resources
|
||||
output: gitops/podinfo-pdx.application.gen.yaml
|
||||
resources:
|
||||
Application:
|
||||
podinfo-pdx:
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: podinfo-pdx
|
||||
namespace: argocd
|
||||
spec:
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
project: default
|
||||
source:
|
||||
path: deploy/components/podinfo-pdx
|
||||
repoURL: https://github.com/brenix/holos-demo.git
|
||||
targetRevision: main
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Concluding Remarks
|
||||
|
||||
In this topic we covered how to use a CUE structure to define attributes of prod
|
||||
and nonprod environments.
|
||||
|
||||
1. We passed the environment name as a parameter to each component using a CUE `@tag`.
|
||||
2. The component definition uses the environment name as a key to get a handle
|
||||
on attributes. For example, the jurisdiction a service operates within.
|
||||
3. The example podinfo component uses an additional structure to map
|
||||
jurisdictions to concrete configuration values.
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
slug: owners
|
||||
title: Owners
|
||||
description: Managing and mapping projects to owners.
|
||||
sidebar_position: 150
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Owners
|
||||
|
||||
## Overview
|
||||
|
||||
## The Code
|
||||
|
||||
### Generating the structure
|
||||
|
||||
### Using an example Component
|
||||
|
||||
## Defining Owners
|
||||
|
||||
### Defining one Owner
|
||||
|
||||
### Defining a collection of Owners
|
||||
|
||||
## Rendering manifests
|
||||
|
||||
## Reviewing the Manifests
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
slug: projects
|
||||
title: Projects
|
||||
description: Managing components organizing them into projects.
|
||||
sidebar_position: 140
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Projects
|
||||
|
||||
## Overview
|
||||
|
||||
## The Code
|
||||
|
||||
### Generating the structure
|
||||
|
||||
### Using an example Component
|
||||
|
||||
## Defining Projects
|
||||
|
||||
### Defining one Project
|
||||
|
||||
### Defining a collection of Projects
|
||||
|
||||
## Rendering manifests
|
||||
|
||||
## Reviewing the Manifests
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
description: Workload Cluster
|
||||
slug: workload-cluster
|
||||
sidebar_position: 250
|
||||
---
|
||||
|
||||
# Workload Cluster
|
||||
|
||||
Key points:
|
||||
|
||||
1. Namespaces
|
||||
2. ExternalSecrets
|
||||
@@ -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.103.0
|
||||
374
doc/md/tutorial/_helm-values/examples/02-helm-values.txt
Normal file
374
doc/md/tutorial/_helm-values/examples/02-helm-values.txt
Normal 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(-)
|
||||
@@ -0,0 +1 @@
|
||||
holos --version
|
||||
@@ -0,0 +1 @@
|
||||
0.103.0
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
git add . && git commit -m 'add blackbox configuration'
|
||||
@@ -0,0 +1,3 @@
|
||||
[main 1adcd08] add blackbox configuration
|
||||
1 file changed, 15 insertions(+)
|
||||
create mode 100644 components/blackbox.cue
|
||||
@@ -0,0 +1 @@
|
||||
cat <<EOF > config/prometheus/blackbox.cue
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
cat <<EOF > components/blackbox/blackbox.cue
|
||||
@@ -0,0 +1,2 @@
|
||||
[main 4221803] integrate blackbox and prometheus together
|
||||
2 files changed, 4 insertions(+), 2 deletions(-)
|
||||
@@ -0,0 +1 @@
|
||||
git add . && git commit -m 'integrate blackbox and prometheus together'
|
||||
@@ -0,0 +1 @@
|
||||
patch -p1 < values.patch
|
||||
@@ -0,0 +1,2 @@
|
||||
patching file 'components/blackbox/values.cue'
|
||||
patching file 'components/prometheus/values.cue'
|
||||
@@ -0,0 +1 @@
|
||||
rm values.patch
|
||||
@@ -0,0 +1 @@
|
||||
EOF
|
||||
@@ -0,0 +1 @@
|
||||
git diff
|
||||
@@ -0,0 +1 @@
|
||||
git init . && git add . && git commit -m "initial commit"
|
||||
64
doc/md/tutorial/_helm-values/script-02-helm-values/git.diff
Normal file
64
doc/md/tutorial/_helm-values/script-02-helm-values/git.diff
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
git add . && git commit -m 'import values'
|
||||
@@ -0,0 +1,4 @@
|
||||
[main 52e90ea] import values
|
||||
2 files changed, 1815 insertions(+)
|
||||
create mode 100644 components/blackbox/values.cue
|
||||
create mode 100644 components/prometheus/values.cue
|
||||
@@ -0,0 +1,3 @@
|
||||
rendered blackbox in 365.936792ms
|
||||
rendered prometheus in 371.855875ms
|
||||
rendered platform in 372.109916ms
|
||||
@@ -0,0 +1,3 @@
|
||||
mkdir holos-helm-values-tutorial
|
||||
cd holos-helm-values-tutorial
|
||||
holos init platform v1alpha5
|
||||
@@ -0,0 +1 @@
|
||||
mkdir -p config/prometheus
|
||||
@@ -0,0 +1 @@
|
||||
mkdir -p components/prometheus components/blackbox
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
cat <<EOF > components/prometheus/prometheus.cue
|
||||
@@ -0,0 +1,12 @@
|
||||
package holos
|
||||
|
||||
Platform: Components: {
|
||||
prometheus: {
|
||||
name: "prometheus"
|
||||
path: "components/prometheus"
|
||||
}
|
||||
blackbox: {
|
||||
name: "blackbox"
|
||||
path: "components/blackbox"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
[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
|
||||
@@ -0,0 +1 @@
|
||||
git add . && git commit -m 'add blackbox and prometheus'
|
||||
@@ -0,0 +1 @@
|
||||
cat <<EOF > platform/prometheus.cue
|
||||
@@ -0,0 +1,5 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
holos render platform
|
||||
@@ -0,0 +1 @@
|
||||
git add . && git commit -m 'render integrated blackbox and prometheus manifests'
|
||||
@@ -0,0 +1,2 @@
|
||||
[main 67efe0d] render integrated blackbox and prometheus manifests
|
||||
2 files changed, 7 insertions(+), 7 deletions(-)
|
||||
@@ -0,0 +1,3 @@
|
||||
rendered blackbox in 374.810666ms
|
||||
rendered prometheus in 382.899334ms
|
||||
rendered platform in 383.270625ms
|
||||
4
doc/md/tutorial/_helm-values/script-02-helm-values/update.sh
Executable file
4
doc/md/tutorial/_helm-values/script-02-helm-values/update.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#! /bin/bash
|
||||
set -euo pipefail
|
||||
[[ -s "$1" ]] && [[ -z "${HOLOS_UPDATE_SCRIPTS:-}" ]] && exit 0
|
||||
cat > "$1"
|
||||
@@ -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"
|
||||
@@ -12,34 +12,34 @@ import TabItem from '@theme/TabItem';
|
||||
|
||||
## Overview
|
||||
|
||||
This tutorial demonstrates mixing additional resources into a component using
|
||||
CUE. Holos components frequently mix in resources so we don't need to modify
|
||||
existing charts or manifests. We'll add an [ExternalSecret] resource to the
|
||||
podinfo Helm chart we configured in the [Hello Holos] tutorial.
|
||||
This tutorial demonstrates how to add additional resources to a component using
|
||||
CUE. Holos components often mix in resources, eliminating the need to modify
|
||||
existing charts or manifests. In this tutorial, we'll add an [ExternalSecret]
|
||||
resource to the podinfo Helm chart configured in the [Hello Holos] tutorial.
|
||||
|
||||
Key concepts:
|
||||
|
||||
1. Resources are validated against `#Resources` defined at the root.
|
||||
2. Custom Resource Definitions need to be imported with timoni.
|
||||
3. Helm, Kustomize, and CUE can be mixed in together in the same component.
|
||||
2. Custom Resource Definitions need to be imported using Timoni.
|
||||
3. Helm, Kustomize, and CUE can be mixed together within the same component.
|
||||
|
||||
## The Code
|
||||
|
||||
### Generating the structure
|
||||
### Generating the Structure
|
||||
|
||||
Use `holos` to generate a minimal platform directory structure. First, create
|
||||
and cd into a blank directory. Then use the `holos generate platform` command to
|
||||
generate a minimal platform.
|
||||
Use `holos` to generate a minimal platform directory structure. First, create
|
||||
and navigate into a blank directory. Then, use the `holos generate platform`
|
||||
command to generate a minimal platform.
|
||||
|
||||
```shell
|
||||
mkdir holos-cue-tutorial && cd holos-cue-tutorial
|
||||
holos init platform v1alpha5
|
||||
```
|
||||
|
||||
### Creating the component
|
||||
### Creating the Component
|
||||
|
||||
Create the directory for the `podinfo` component. Create an empty file and then
|
||||
add the following CUE configuration to it.
|
||||
Create the directory for the `podinfo` component. Create an empty file, then add
|
||||
the following CUE configuration to it.
|
||||
|
||||
```bash
|
||||
mkdir -p components/podinfo
|
||||
@@ -72,7 +72,7 @@ Component: #Helm & {
|
||||
EOF
|
||||
```
|
||||
|
||||
Integrate the component with the platform.
|
||||
Register the component with the platform.
|
||||
|
||||
```bash
|
||||
cat <<EOF > platform/podinfo.cue
|
||||
@@ -94,7 +94,7 @@ Render the platform.
|
||||
<Tabs groupId="tutorial-hello-render-manifests">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform ./platform
|
||||
holos render platform
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
@@ -161,12 +161,13 @@ field validates against the `#Resources` definition in [resources.cue].
|
||||
|
||||
### Importing CRDs
|
||||
|
||||
Holos includes CUE schema definitions of the ExternalSecret custom resource
|
||||
definition (CRD). These schemas are located in the `cue.mod` directory, written by
|
||||
the `holos init platform` command we executed at the start of this tutorial.
|
||||
Holos includes CUE schema definitions for the ExternalSecret Custom Resource
|
||||
Definition (CRD). These schemas are located in the `cue.mod` directory,
|
||||
generated by the `holos init platform` command executed at the start of this
|
||||
tutorial.
|
||||
|
||||
Import your own custom resource definitions using [timoni]. We imported the
|
||||
ExternalSecret CRDs embedded into `holos` with the following command.
|
||||
To import your own custom resource definitions, use [Timoni]. We imported the
|
||||
ExternalSecret CRDs embedded in `holos` using the following command.
|
||||
|
||||
<Tabs groupId="35B1A1A1-D7DF-4D27-A575-28556E182096">
|
||||
<TabItem value="command" label="Command">
|
||||
@@ -203,16 +204,16 @@ Take a look at
|
||||
the imported definitions.
|
||||
:::
|
||||
|
||||
Once imported, the last step is to add the resource kind to the `#Resources`
|
||||
struct. This is most often accomplished by adding a new file which cue unifies
|
||||
with the existing [resources.cue] file.
|
||||
Once imported, the final step is to add the resource kind to the `#Resources`
|
||||
struct. Typically, this is done by creating a new file that CUE unifies with the
|
||||
existing [resources.cue] file.
|
||||
|
||||
## Reviewing Changes
|
||||
|
||||
Render the platform with the `ExternalSecret` mixed into the podinfo component.
|
||||
|
||||
```shell
|
||||
holos render platform ./platform
|
||||
holos render platform
|
||||
```
|
||||
|
||||
Take a look at the diff to see the mixed in `ExternalSecret`.
|
||||
@@ -249,22 +250,20 @@ index 6e4aec0..f79e9d0 100644
|
||||
```
|
||||
|
||||
We saw how to mix in resources using the `Resources` field of the
|
||||
[ComponentConfig]. This technique approach works for every kind of component in
|
||||
Holos. We did this without needing to fork the upstream Helm chart so we can
|
||||
easily update to new podinfo versions as they're released.
|
||||
[ComponentConfig]. This approach works for every kind of component in Holos,
|
||||
allowing seamless integration without needing to fork the upstream Helm chart.
|
||||
This way, we can easily update to new podinfo versions as they're released.
|
||||
|
||||
## Trying Locally
|
||||
|
||||
Optionally apply the manifests Holos rendered to a [Local Cluster].
|
||||
Optionally, apply the manifests rendered by Holos to a [Local Cluster] for
|
||||
testing.
|
||||
|
||||
## Next Steps
|
||||
|
||||
This tutorial uses the `#Resources` structure to map resource kinds to their
|
||||
schema definitions in CUE. This structure is defined in `resources.cue` at the
|
||||
root of the tree. Take a look at [resources.cue] to see this mapping structure.
|
||||
|
||||
Continue to the next tutorial to learn how to define your own data structures
|
||||
similar to this `#Resources` structure.
|
||||
schema definitions in CUE. This structure is defined in `resources.cue` at the
|
||||
root of the tree. Take a look at [resources.cue] to see this mapping structure.
|
||||
|
||||
[Local Cluster]: ../topics/local-cluster.mdx
|
||||
[ExternalSecret]: https://external-secrets.io/latest/api/externalsecret/
|
||||
|
||||
@@ -15,32 +15,22 @@ import ComponentSequence from '@site/src/diagrams/render-component-sequence.mdx'
|
||||
|
||||
## Overview
|
||||
|
||||
One of the first exercises we do when learning a new programming language is
|
||||
printing out a "Hello World!" greeting. Hello Holos configures the [podinfo Helm
|
||||
chart][podinfo] producing a similar greeting from a Kubernetes Service.
|
||||
Like a traditional "Hello World" program, we'll start by configuring the
|
||||
[podinfo Helm chart][podinfo] to output a greeting from a Kubernetes Service.
|
||||
This introduces the core concept of wrapping Helm charts as Holos Components.
|
||||
|
||||
By the end of this tutorial you have an understanding of how to wrap a Helm
|
||||
Chart as a Holos Component.
|
||||
## Implementation
|
||||
|
||||
## The Code
|
||||
### Initialize Platform Structure
|
||||
|
||||
### Generating the structure
|
||||
|
||||
Use `holos` to generate a minimal platform directory structure. Start by
|
||||
creating a blank directory to hold the platform configuration.
|
||||
Create and initialize a minimal platform:
|
||||
|
||||
```shell
|
||||
mkdir holos-tutorial && cd holos-tutorial
|
||||
```
|
||||
|
||||
Use the `holos init platform` command to initialize a minimal platform in the
|
||||
blank directory.
|
||||
|
||||
```shell
|
||||
holos init platform v1alpha5
|
||||
```
|
||||
|
||||
Here's the filesystem tree we'll build in this tutorial.
|
||||
The resulting directory structure:
|
||||
|
||||
<Tabs groupId="80D04C6A-BC83-44D0-95CC-CE01B439B159">
|
||||
<TabItem value="tree" label="Tree">
|
||||
@@ -104,10 +94,9 @@ initialization.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Creating a component
|
||||
### Create the Component
|
||||
|
||||
Start by creating a directory for the `podinfo` component. Create an empty file
|
||||
and then add the following CUE configuration to it.
|
||||
Configure the `podinfo` component:
|
||||
|
||||
```bash
|
||||
mkdir -p components/podinfo
|
||||
@@ -145,20 +134,19 @@ EOF
|
||||
```
|
||||
|
||||
:::important
|
||||
CUE loads all of `*.cue` files in the component directory to define component,
|
||||
similar to Go packages.
|
||||
Like Go packages, CUE loads all `*.cue` files in the component directory to
|
||||
define the component.
|
||||
:::
|
||||
|
||||
:::note
|
||||
CUE _also_ loads all `*.cue` files from the component leaf directory to the
|
||||
platform root directory. In this example, `#Helm` on line 6 is defined in
|
||||
`schema.cue` at the root.
|
||||
CUE recursively loads `*.cue` files from the component directory up to the
|
||||
platform root. For example, `#Helm` referenced on line 6 is defined in
|
||||
root-level `schema.cue`.
|
||||
:::
|
||||
|
||||
### Integrating the component
|
||||
### Add to Platform
|
||||
|
||||
Integrate the `podinfo` component into the platform by creating a new CUE file
|
||||
in the `platform` directory with the following content.
|
||||
Register the `podinfo` component in `platform/podinfo.cue`:
|
||||
|
||||
```bash
|
||||
cat <<EOF > platform/podinfo.cue
|
||||
@@ -178,19 +166,17 @@ EOF
|
||||
```
|
||||
|
||||
:::tip
|
||||
Component parameters may have any name as long as they don't start with
|
||||
`holos_`.
|
||||
Parameter names are unrestricted, except for the reserved `holos_` prefix.
|
||||
:::
|
||||
|
||||
## Rendering manifests
|
||||
## Generate Manifests
|
||||
|
||||
Render a manifest for `podinfo` using the `holos render platform ./platform`
|
||||
command. The `platform/` directory is the main entrypoint for this command.
|
||||
Render the `podinfo` configuration:
|
||||
|
||||
<Tabs groupId="E150C802-7162-4FBF-82A7-77D9ADAEE847">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform ./platform
|
||||
holos render platform
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
@@ -202,10 +188,7 @@ rendered platform in 1.938759417s
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::important
|
||||
Holos rendered the following manifest file by executing `helm template` after
|
||||
caching `podinfo` locally.
|
||||
:::
|
||||
Holos executes `helm template` with locally cached charts to generate:
|
||||
|
||||
```txt
|
||||
deploy/components/podinfo/podinfo.gen.yaml
|
||||
@@ -348,21 +331,24 @@ grep -B2 Hello deploy/components/podinfo/podinfo.gen.yaml
|
||||
|
||||
## Breaking it down
|
||||
|
||||
We run `holos render platform ./platform` because the CUE files in the platform
|
||||
directory export a [Platform] resource to `holos`. The platform directory is
|
||||
the entrypoint to the platform rendering process.
|
||||
We run `holos render platform` because the CUE files in the platform directory
|
||||
export a [Platform] resource to `holos`.
|
||||
|
||||
Components are the building blocks for a Platform. The `platform/podinfo.cue`
|
||||
file integrates the `podinfo` Component with the Platform.
|
||||
:::important
|
||||
The `platform/` directory is the default entry point to the platform rendering
|
||||
process. Override with `--platform <dir>`.
|
||||
:::
|
||||
|
||||
Holos requires two fields to integrate a component with the platform.
|
||||
Components are the building blocks of a Platform. The `platform/podinfo.cue`
|
||||
file integrates the `podinfo` component with the Platform.
|
||||
|
||||
Holos requires two fields to integrate a component with the platform:
|
||||
|
||||
1. A unique name for the component.
|
||||
2. The component path to the directory containing the CUE files exporting a
|
||||
2. The component path to the directory containing the CUE files that export a
|
||||
`BuildPlan` defining the component.
|
||||
|
||||
Component parameters are optional. They allow re-use of the same component.
|
||||
Refer to the [Component Parameters] topic for more information.
|
||||
Component parameters are optional and allow re-use of the same component.
|
||||
|
||||
<Tabs groupId="67C1EE71-3EA8-4568-9F6D-0072BA09FF12">
|
||||
<TabItem value="overview" label="Rendering Overview">
|
||||
@@ -379,12 +365,12 @@ Refer to the [Component Parameters] topic for more information.
|
||||
|
||||
## Next Steps
|
||||
|
||||
We've shown how to integrate one Helm chart to the Platform, but we haven't yet
|
||||
covered multiple Helm charts. Continue on with the next tutorial to learn how
|
||||
Holos makes it easy to inject values into multiple components safely and easily.
|
||||
We've shown how to integrate one Helm chart into the Platform, but we haven't
|
||||
yet covered multiple Helm charts. Continue with the next tutorial to learn how
|
||||
Holos makes it easy to inject values into multiple components safely and
|
||||
efficiently.
|
||||
|
||||
[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/
|
||||
[Platform]: ../api/author.md#Platform
|
||||
[Component Parameters]: ../topics/component-parameters.mdx
|
||||
|
||||
@@ -7,6 +7,8 @@ sidebar_position: 40
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import YouTube from '@site/src/components/YouTube';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
<head>
|
||||
<meta property="og:title" content="Helm Values | Holos" />
|
||||
@@ -17,260 +19,179 @@ import TabItem from '@theme/TabItem';
|
||||
|
||||
## Overview
|
||||
|
||||
Holos makes it easier to integrate multiple Helm charts together. Holos adds
|
||||
valuable capabilities to Helm and Kustomize.
|
||||
Holos simplifies integrating multiple Helm charts by adding valuable
|
||||
capabilities to Helm and Kustomize:
|
||||
|
||||
1. Inject the same value into two or more charts to integrate them safer than Helm alone.
|
||||
2. Add strong type checking and validation of constraints for Helm input values.
|
||||
3. Implementation of the [rendered manifests pattern].
|
||||
1. Inject the same value into multiple charts more safely than using Helm alone.
|
||||
2. Add strong type checking and validation for Helm input values.
|
||||
3. Implement the [rendered manifests pattern].
|
||||
|
||||
We'll manage the [prometheus] and [blackbox] Helm Charts in this tutorial.
|
||||
Unfortunately, the upstream `values.yaml` files are misconfigured by default.
|
||||
Prometheus tries to connect to Blackbox at the wrong host and port.
|
||||
In this tutorial, we'll manage the [prometheus] and [blackbox] Helm charts. By
|
||||
default, the upstream `values.yaml` files are misconfigured, causing Prometheus
|
||||
to connect to Blackbox at the wrong host and port.
|
||||
|
||||
:::tip
|
||||
Holos and CUE makes it easy to fix this problem by integrating them correctly.
|
||||
The integrated charts will stay in lock step over time.
|
||||
:::
|
||||
## The Video
|
||||
|
||||
The video below enhances this tutorial by offering greater detail on the issue
|
||||
of poorly integrated Helm charts and the solution we've provided. If you're
|
||||
looking for a deeper explanation of the code being presented, this video is a great
|
||||
resource.
|
||||
|
||||
{/* cspell:disable-next-line */}
|
||||
<YouTube id="PSdceGlhHGo"/>
|
||||
|
||||
## The Code
|
||||
|
||||
### Holos Version
|
||||
|
||||
Ensure you have a current version of `holos` installed. This document was
|
||||
tested with the following version.
|
||||
|
||||
import HolosVersionCommand from '!!raw-loader!./_helm-values/script-01-holos-version/command.sh';
|
||||
import HolosVersionOutput from '!!raw-loader!./_helm-values/script-01-holos-version/output.txt';
|
||||
|
||||
<CodeBlock language="bash">{HolosVersionCommand}</CodeBlock>
|
||||
<CodeBlock language="txt">{HolosVersionOutput}</CodeBlock>
|
||||
|
||||
### Generating the structure
|
||||
|
||||
Use `holos` to generate a minimal platform directory structure. First, create
|
||||
and cd into a blank directory. Then use the `holos init platform` command.
|
||||
Use `holos` to generate a minimal platform directory structure. First, create
|
||||
and navigate into a blank directory, then use the `holos init platform` command:
|
||||
|
||||
```shell
|
||||
mkdir holos-helm-values-tutorial
|
||||
cd holos-helm-values-tutorial
|
||||
holos init platform v1alpha5
|
||||
```
|
||||
import MkdirAndInit from '!!raw-loader!./_helm-values/script-02-helm-values/mkdir-and-init.sh';
|
||||
|
||||
Make a commit to track changes.
|
||||
<CodeBlock language="bash">{MkdirAndInit}</CodeBlock>
|
||||
|
||||
```bash
|
||||
git init . && git add . && git commit -m initial
|
||||
```
|
||||
Make an initial commit to track changes:
|
||||
|
||||
import GitInit from '!!raw-loader!./_helm-values/script-02-helm-values/git-init.sh';
|
||||
|
||||
<CodeBlock language="bash">{GitInit}</CodeBlock>
|
||||
|
||||
### Managing the Components
|
||||
|
||||
Create the `prometheus` and `blackbox` component directories, then add each of
|
||||
the following file contents.
|
||||
|
||||
```bash
|
||||
mkdir -p components/prometheus components/blackbox
|
||||
```
|
||||
import MkdirComponents from '!!raw-loader!./_helm-values/script-02-helm-values/mkdir-components.sh';
|
||||
import PrometheusComponentHeader from '!!raw-loader!./_helm-values/script-02-helm-values/prometheus-component-header.sh';
|
||||
import PrometheusComponentBody from '!!raw-loader!./_helm-values/script-02-helm-values/prometheus-component-body.cue';
|
||||
import BlackboxComponentHeader from '!!raw-loader!./_helm-values/script-02-helm-values/blackbox-component-header.sh';
|
||||
import BlackboxComponentBody from '!!raw-loader!./_helm-values/script-02-helm-values/blackbox-component-body.cue';
|
||||
import EofTrailer from '!!raw-loader!./_helm-values/script-02-helm-values/eof-trailer.sh';
|
||||
|
||||
|
||||
<CodeBlock language="bash">{MkdirComponents}</CodeBlock>
|
||||
|
||||
<Tabs groupId="D15A3008-1EFC-4D34-BED1-15BC0C736CC3">
|
||||
<TabItem value="prometheus.cue" label="prometheus.cue">
|
||||
```bash
|
||||
cat <<EOF > components/prometheus/prometheus.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```bash
|
||||
EOF
|
||||
```
|
||||
<CodeBlock language="bash">{PrometheusComponentHeader}</CodeBlock>
|
||||
<CodeBlock language="cue" showLineNumbers>{PrometheusComponentBody}</CodeBlock>
|
||||
<CodeBlock language="bash">{EofTrailer}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="blackbox.cue" label="blackbox.cue">
|
||||
```bash
|
||||
cat <<EOF > components/blackbox/blackbox.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```bash
|
||||
EOF
|
||||
```
|
||||
<CodeBlock language="bash">{BlackboxComponentHeader}</CodeBlock>
|
||||
<CodeBlock language="cue" showLineNumbers>{BlackboxComponentBody}</CodeBlock>
|
||||
<CodeBlock language="bash">{EofTrailer}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Integrating the Components
|
||||
### Register the Components
|
||||
|
||||
Integrate the components with the platform by adding the following file to the
|
||||
platform directory.
|
||||
Register the components with the platform by adding the following file to the platform directory.
|
||||
|
||||
```bash
|
||||
cat <<EOF > platform/prometheus.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
import RegisterComponentsHeader from '!!raw-loader!./_helm-values/script-02-helm-values/register-components-header.sh';
|
||||
import RegisterComponentsBody from '!!raw-loader!./_helm-values/script-02-helm-values/register-components-body.cue';
|
||||
|
||||
Platform: Components: {
|
||||
prometheus: {
|
||||
name: "prometheus"
|
||||
path: "components/prometheus"
|
||||
}
|
||||
blackbox: {
|
||||
name: "blackbox"
|
||||
path: "components/blackbox"
|
||||
}
|
||||
}
|
||||
```
|
||||
```bash
|
||||
EOF
|
||||
```
|
||||
<CodeBlock language="bash">{RegisterComponentsHeader}</CodeBlock>
|
||||
<CodeBlock language="cue" showLineNumbers>{RegisterComponentsBody}</CodeBlock>
|
||||
<CodeBlock language="bash">{EofTrailer}</CodeBlock>
|
||||
|
||||
Render the platform.
|
||||
|
||||
import RenderCommand from '!!raw-loader!./_helm-values/script-02-helm-values/render.sh';
|
||||
import RegisterComponentsRenderOutput from '!!raw-loader!./_helm-values/script-02-helm-values/register-components-output.txt';
|
||||
|
||||
<Tabs groupId="33D6BFED-62D8-4A42-A26A-F3121D57C4E5">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform ./platform
|
||||
```
|
||||
<CodeBlock language="bash">{RenderCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="output" label="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
|
||||
```
|
||||
<CodeBlock language="txt">{RegisterComponentsRenderOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Commit the results.
|
||||
|
||||
import GitCommitRegisterComponents from '!!raw-loader!./_helm-values/script-02-helm-values/register-components-git-commit.sh';
|
||||
import RegisterComponentsGitOutput from '!!raw-loader!./_helm-values/script-02-helm-values/register-components-git-commit-output.txt';
|
||||
|
||||
<Tabs groupId="446CC550-A634-45C0-BEC7-992E5C56D4FA">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
git add . && git commit -m 'add blackbox and prometheus'
|
||||
```
|
||||
<CodeBlock language="bash">{GitCommitRegisterComponents}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="output" label="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
|
||||
```
|
||||
<CodeBlock language="txt">{RegisterComponentsGitOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Importing Helm Values
|
||||
|
||||
Holos renders the helm charts with their default values. We can import these
|
||||
default values into CUE so we can easily work with them as structured data
|
||||
instead of text markup.
|
||||
Holos renders Helm charts with their default values. We can import these default
|
||||
values into CUE to work with them as structured data instead of text markup.
|
||||
|
||||
```bash
|
||||
holos cue import \
|
||||
--package holos \
|
||||
--path 'Helm: Values:' \
|
||||
--outfile components/prometheus/values.cue \
|
||||
components/prometheus/vendor/25.27.0/prometheus/values.yaml
|
||||
```
|
||||
import ImportPrometheusValues from '!!raw-loader!./_helm-values/script-02-helm-values/import-prometheus-values.sh';
|
||||
import ImportBlackboxValues from '!!raw-loader!./_helm-values/script-02-helm-values/import-blackbox-values.sh';
|
||||
|
||||
```bash
|
||||
holos cue import \
|
||||
--package holos \
|
||||
--path 'Helm: Values:' \
|
||||
--outfile components/blackbox/values.cue \
|
||||
components/blackbox/vendor/9.0.1/prometheus-blackbox-exporter/values.yaml
|
||||
```
|
||||
<CodeBlock language="bash">{ImportPrometheusValues}</CodeBlock>
|
||||
<CodeBlock language="bash">{ImportBlackboxValues}</CodeBlock>
|
||||
|
||||
These commands convert the YAML data into CUE code and nest the values under the
|
||||
`Values` field of the `Helm` struct.
|
||||
|
||||
:::important
|
||||
CUE unifies `values.cue` with the other `*.cue` files in the same directory.
|
||||
CUE unifies `values.cue` with the other `\*.cue` files in the same directory.
|
||||
:::
|
||||
|
||||
Render the platform and commit the results.
|
||||
Render the platform using `holos render platform` and commit the results.
|
||||
|
||||
import ImportValuesRenderOutput from '!!raw-loader!./_helm-values/script-02-helm-values/import-values-render-output.txt';
|
||||
import ImportValuesGitCommit from '!!raw-loader!./_helm-values/script-02-helm-values/import-values-git-commit.sh';
|
||||
import ImportValuesGitOutput from '!!raw-loader!./_helm-values/script-02-helm-values/import-values-git-output.txt';
|
||||
|
||||
<Tabs groupId="BDDCD65A-2E9D-4BA6-AAE2-8099494D5E4B">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform ./platform
|
||||
```
|
||||
<CodeBlock language="bash">{RenderCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```txt
|
||||
rendered blackbox in 365.936792ms
|
||||
rendered prometheus in 371.855875ms
|
||||
rendered platform in 372.109916ms
|
||||
```
|
||||
<CodeBlock language="txt">{ImportValuesRenderOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<Tabs groupId="1636C619-258E-4D49-8052-F64B588C9177">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
git add . && git commit -m 'import values'
|
||||
```
|
||||
<CodeBlock language="bash">{ImportValuesGitCommit}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="output" label="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
|
||||
```
|
||||
<CodeBlock language="txt">{ImportValuesGitOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Managing Common Configuration
|
||||
|
||||
Define a structure to hold the configuration values we want both helm charts to
|
||||
use. We add this configuration to the `components` directory so it's in scope
|
||||
for all components.
|
||||
To manage shared configuration for both Helm charts, define a structure that
|
||||
holds the common configuration values. Create a `config` directory at the root
|
||||
of the repository, and place the configuration file there to ensure it is
|
||||
accessible to all components.
|
||||
|
||||
```bash
|
||||
cat <<EOF > components/blackbox.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
import BlackboxCommonConfigMkdir from '!!raw-loader!./_helm-values/script-02-helm-values/mkdir-common-config.sh';
|
||||
import BlackboxCommonConfigHeader from '!!raw-loader!./_helm-values/script-02-helm-values/blackbox-common-config-header.sh';
|
||||
import BlackboxCommonConfigBody from '!!raw-loader!./_helm-values/script-02-helm-values/blackbox-common-config-body.cue';
|
||||
|
||||
// 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
|
||||
}
|
||||
```
|
||||
```bash
|
||||
EOF
|
||||
```
|
||||
<CodeBlock language="bash">{BlackboxCommonConfigMkdir}</CodeBlock>
|
||||
<CodeBlock language="bash">{BlackboxCommonConfigHeader}</CodeBlock>
|
||||
<CodeBlock language="cue" showLineNumbers>{BlackboxCommonConfigBody}</CodeBlock>
|
||||
<CodeBlock language="bash" showLineNumbers>{EofTrailer}</CodeBlock>
|
||||
|
||||
:::important
|
||||
1. CUE loads and unifies all `*.cue` files from the root directory containing
|
||||
@@ -281,75 +202,41 @@ languages with only type checking.
|
||||
|
||||
Add and commit the configuration.
|
||||
|
||||
import BlackboxCommonConfigGit from '!!raw-loader!./_helm-values/script-02-helm-values/blackbox-common-config-git-commit.sh';
|
||||
import BlackboxCommonConfigGitOutput from '!!raw-loader!./_helm-values/script-02-helm-values/blackbox-common-config-git-output.txt';
|
||||
|
||||
<Tabs groupId="A738CCE4-F0C6-4CC7-BE1F-2B92F0E86FDC">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
git add . && git commit -m 'add blackbox configuration'
|
||||
```
|
||||
<CodeBlock language="bash">{BlackboxCommonConfigGit}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```txt
|
||||
[main 1adcd08] add blackbox configuration
|
||||
1 file changed, 15 insertions(+)
|
||||
create mode 100644 components/blackbox.cue
|
||||
```
|
||||
<CodeBlock language="bash">{BlackboxCommonConfigGitOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Referring to Common Configuration
|
||||
### Using Common Configuration Across Components
|
||||
|
||||
Referencing common configuration from multiple components is easy and safe with
|
||||
Holos and CUE.
|
||||
Referencing common configuration across multiple components is straightforward
|
||||
and reliable using Holos and CUE. Configuration can be imported where necessary
|
||||
following [CUE module standards], which are similar to Golang.
|
||||
|
||||
Patch the two `values.cue` files, or edit them by hand, to reference
|
||||
`Blackbox.host` and `Blackbox.port`.
|
||||
To apply the common configuration, patch the two `values.cue` files, or manually
|
||||
edit them to import the configuration and reference `prometheus.blackbox.host`
|
||||
and `prometheus.blackbox.port`.
|
||||
|
||||
import CommonConfigPatchCommand from '!!raw-loader!./_helm-values/script-02-helm-values/common-config-patch.sh';
|
||||
import CommonConfigPatchDiff from '!!raw-loader!./_helm-values/script-02-helm-values/values.patch';
|
||||
import CommonConfigPatchOutput from '!!raw-loader!./_helm-values/script-02-helm-values/common-config-patch.txt';
|
||||
|
||||
<Tabs groupId="5FFCE892-B8D4-4F5B-B2E2-39EC9E9F87A4">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
patch -p1 < values.patch
|
||||
```
|
||||
<CodeBlock language="bash">{CommonConfigPatchCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="patch" label="values.patch">
|
||||
```diff
|
||||
--- a/components/blackbox/values.cue
|
||||
+++ b/components/blackbox/values.cue
|
||||
@@ -1,6 +1,8 @@
|
||||
package holos
|
||||
|
||||
Helm: Values: {
|
||||
+ fullnameOverride: 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 +194,7 @@ Helm: Values: {
|
||||
annotations: {}
|
||||
labels: {}
|
||||
type: "ClusterIP"
|
||||
- port: 9115
|
||||
+ port: Blackbox.port
|
||||
ipDualStack: {
|
||||
enabled: false
|
||||
ipFamilies: ["IPv6", "IPv4"]
|
||||
--- a/components/prometheus/values.cue
|
||||
+++ b/components/prometheus/values.cue
|
||||
@@ -1083,7 +1083,7 @@ Helm: Values: {
|
||||
target_label: "__param_target"
|
||||
}, {
|
||||
target_label: "__address__"
|
||||
- replacement: "blackbox"
|
||||
+ replacement: "\(Blackbox.host):\(Blackbox.port)"
|
||||
}, {
|
||||
source_labels: ["__param_target"]
|
||||
target_label: "instance"
|
||||
```
|
||||
<CodeBlock language="diff">{CommonConfigPatchDiff}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```txt
|
||||
patching file 'components/blackbox/values.cue'
|
||||
patching file 'components/prometheus/values.cue'
|
||||
```
|
||||
<CodeBlock language="txt">{CommonConfigPatchOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
@@ -360,166 +247,92 @@ safely and easily.
|
||||
|
||||
Remove the patch file, then commit the changes.
|
||||
|
||||
import CommonConfigPatchRm from '!!raw-loader!./_helm-values/script-02-helm-values/common-config-rm.sh';
|
||||
import CommonConfigPatchGitCommit from '!!raw-loader!./_helm-values/script-02-helm-values/common-config-git.sh';
|
||||
import CommonConfigPatchGitCommitOutput from '!!raw-loader!./_helm-values/script-02-helm-values/common-config-git-output.txt';
|
||||
|
||||
<Tabs groupId="6498B00E-FADA-4EB2-885C-808F1D22E04D">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
rm values.patch
|
||||
```
|
||||
```bash
|
||||
git add . && git commit -m 'integrate blackbox and prometheus together'
|
||||
```
|
||||
<CodeBlock language="bash">{CommonConfigPatchRm}</CodeBlock>
|
||||
<CodeBlock language="bash">{CommonConfigPatchGitCommit}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```txt
|
||||
[main 4221803] integrate blackbox and prometheus together
|
||||
2 files changed, 4 insertions(+), 2 deletions(-)
|
||||
```
|
||||
<CodeBlock language="txt">{CommonConfigPatchGitCommitOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Reviewing Changes
|
||||
|
||||
Holos makes it easy to see and review platform wide changes. Render the
|
||||
platform to see how both prometheus and blackbox change in lock step.
|
||||
Holos makes it easy to view and review platform-wide changes. Render the
|
||||
platform to observe how both Prometheus and Blackbox update in sync.
|
||||
|
||||
import ReviewingChangesRenderOutput from '!!raw-loader!./_helm-values/script-02-helm-values/reviewing-changes-render-output.txt';
|
||||
|
||||
<Tabs groupId="E7F6D8B1-22FA-4075-9B44-D9F2815FE0D3">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform ./platform
|
||||
```
|
||||
<CodeBlock language="bash">{RenderCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```txt
|
||||
rendered blackbox in 374.810666ms
|
||||
rendered prometheus in 382.899334ms
|
||||
rendered platform in 383.270625ms
|
||||
```
|
||||
<CodeBlock language="txt">{ReviewingChangesRenderOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Changes are easily visible with version control.
|
||||
Changes are easily visible in version control.
|
||||
|
||||
import GitDiffCommand from '!!raw-loader!./_helm-values/script-02-helm-values/git-diff.sh';
|
||||
import GitDiff from '!!raw-loader!./_helm-values/script-02-helm-values/git.diff';
|
||||
|
||||
<Tabs groupId="9789A0EF-24D4-4FB9-978A-3895C2778789">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
git diff
|
||||
```
|
||||
<CodeBlock language="bash">{GitDiffCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```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
|
||||
|
||||
```
|
||||
<CodeBlock language="diff">{GitDiff}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
From the diff we know this change will:
|
||||
From the diff, we can see this change will:
|
||||
|
||||
1. Reconfigure the blackbox exporter host from `prometheus-blackbox-exporter` to `blackbox`.
|
||||
2. Have no effect on the blackbox service port. It was already using the default 9115.
|
||||
3. Reconfigure prometheus to query the blackbox exporter at the correct host and
|
||||
port, `blackbox:9115`.
|
||||
1. Reconfigure the Blackbox Exporter host from `prometheus-blackbox-exporter` to `blackbox`.
|
||||
2. Have no effect on the Blackbox service port, as it was already using the default `9115`.
|
||||
3. Reconfigure Prometheus to query the Blackbox Exporter at the correct host and port, `blackbox:9115`.
|
||||
|
||||
Without this change prometheus incorrectly assumed blackbox was listening at
|
||||
Without this change, Prometheus incorrectly assumed Blackbox was listening at
|
||||
`blackbox` on port `80` when it was actually listening at
|
||||
`prometheus-blackbox-exporter` port `9115`. Going forward, changing the
|
||||
blackbox host or port will reconfigure both charts correctly.
|
||||
`prometheus-blackbox-exporter` on port `9115`. Going forward, changing the
|
||||
Blackbox host or port will reconfigure both charts correctly.
|
||||
|
||||
Commit the changes and move on to deploying them.
|
||||
Commit the changes and proceed to deploy them.
|
||||
|
||||
import ReviewingChangesGitCommit from '!!raw-loader!./_helm-values/script-02-helm-values/reviewing-changes-git-commit.sh';
|
||||
import ReviewingChangesGitOutput from '!!raw-loader!./_helm-values/script-02-helm-values/reviewing-changes-git-output.txt';
|
||||
|
||||
<Tabs groupId="F8C9A98D-DE1E-4EF6-92C1-017A9166F6C7">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
git add . && git commit -m 'render integrated blackbox and prometheus manifests'
|
||||
```
|
||||
<CodeBlock language="bash">{ReviewingChangesGitCommit}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```txt
|
||||
[main 67efe0d] render integrated blackbox and prometheus manifests
|
||||
2 files changed, 7 insertions(+), 7 deletions(-)
|
||||
```
|
||||
<CodeBlock language="txt">{ReviewingChangesGitOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Trying Locally
|
||||
|
||||
Optionally apply the manifests Holos rendered to a [Local Cluster].
|
||||
Optionally, apply the manifests rendered by Holos to a [Local Cluster].
|
||||
|
||||
## Next Steps
|
||||
|
||||
In this tutorial we learned how Holos makes it easier to holistically integrate
|
||||
the [prometheus] and [blackbox] charts so they're configured in lock step with
|
||||
each other. If we relied on Helm alone, there is no good way to configure both
|
||||
charts to use the same service endpoint.
|
||||
In this tutorial, we learned how Holos simplifies the holistic integration of
|
||||
the [prometheus] and [blackbox] charts, ensuring they are configured
|
||||
consistently. By using Holos, we overcome the limitations of relying solely on
|
||||
Helm, which lacks an effective method to configure both charts to use the same
|
||||
service endpoint.
|
||||
|
||||
[rendered manifests pattern]: https://akuity.io/blog/the-rendered-manifests-pattern
|
||||
[prometheus]: https://github.com/prometheus-community/helm-charts/tree/prometheus-25.27.0/charts/prometheus
|
||||
[blackbox]: https://github.com/prometheus-community/helm-charts/tree/prometheus-blackbox-exporter-9.0.1/charts/prometheus-blackbox-exporter
|
||||
[httpbin]: https://github.com/mccutchen/go-httpbin/tree/v2.15.0
|
||||
|
||||
[CUE module standards]: https://cuelang.org/docs/concept/modules-packages-instances/
|
||||
[Config Schema]: #config-schema
|
||||
|
||||
[Technical Overview]: ./overview.mdx
|
||||
|
||||
87
doc/md/tutorial/helm-values_test.go
Normal file
87
doc/md/tutorial/helm-values_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/holos-run/holos/cmd"
|
||||
"github.com/rogpeppe/go-internal/testscript"
|
||||
|
||||
cue "cuelang.org/go/cmd/cue/cmd"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(testscript.RunMain(m, map[string]func() int{
|
||||
"holos": cmd.MakeMain(),
|
||||
"cue": cue.Main,
|
||||
}))
|
||||
}
|
||||
|
||||
// Run these with go test -v to see the verbose names
|
||||
func TestHelmValues(t *testing.T) {
|
||||
t.Run("TestHelmValues", func(t *testing.T) {
|
||||
// Get an ordered list of test script files.
|
||||
dir := "_helm-values"
|
||||
for _, file := range sortedTestScripts(t, filepath.Join(dir, "examples")) {
|
||||
t.Run("examples", func(t *testing.T) {
|
||||
runOneScript(t, dir, file)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func runOneScript(t *testing.T, dir string, file string) {
|
||||
params := testscript.Params{
|
||||
Dir: "",
|
||||
Files: []string{file},
|
||||
RequireExplicitExec: true,
|
||||
RequireUniqueNames: false,
|
||||
WorkdirRoot: filepath.Join(testDir(t), dir),
|
||||
UpdateScripts: os.Getenv("HOLOS_UPDATE_SCRIPTS") != "",
|
||||
Setup: func(env *testscript.Env) error {
|
||||
// Needed for update.sh to determine if we need to update output files.
|
||||
env.Setenv("HOLOS_UPDATE_SCRIPTS", os.Getenv("HOLOS_UPDATE_SCRIPTS"))
|
||||
// Just like cmd/cue/cmd.TestScript, set up separate cache and config dirs per test.
|
||||
env.Setenv("CUE_CACHE_DIR", filepath.Join(env.WorkDir, "tmp/cachedir"))
|
||||
configDir := filepath.Join(env.WorkDir, "tmp/configdir")
|
||||
env.Setenv("CUE_CONFIG_DIR", configDir)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
testscript.Run(t, params)
|
||||
}
|
||||
|
||||
// testDir returns the path of the directory containing the go source file of
|
||||
// the caller.
|
||||
func testDir(t *testing.T) string {
|
||||
_, file, _, ok := runtime.Caller(0)
|
||||
if !ok {
|
||||
t.Fatal("could not get runtime caller")
|
||||
}
|
||||
return filepath.Dir(file)
|
||||
}
|
||||
|
||||
func sortedTestScripts(t *testing.T, dir string) (files []string) {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if os.IsNotExist(err) {
|
||||
// Continue to helpful error on len(files) == 0 below.
|
||||
} else if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
if strings.HasSuffix(name, ".txtar") || strings.HasSuffix(name, ".txt") {
|
||||
files = append(files, filepath.Join(dir, name))
|
||||
}
|
||||
}
|
||||
if len(files) == 0 {
|
||||
t.Fatalf("no txtar nor txt scripts found in dir %s", dir)
|
||||
}
|
||||
slices.Sort(files)
|
||||
return files
|
||||
}
|
||||
@@ -12,14 +12,14 @@ import TabItem from '@theme/TabItem';
|
||||
|
||||
## Overview
|
||||
|
||||
In the previous tutorial we learned how Holos makes it easier to holistically
|
||||
integrate the [prometheus] and [blackbox] charts so they're configured in lock
|
||||
step with each other.
|
||||
In the previous tutorial, we learned how Holos simplifies the holistic
|
||||
integration of the [prometheus] and [blackbox] charts, ensuring they are
|
||||
configured in sync.
|
||||
|
||||
This tutorial goes further by integrating the [httpbin] service with prometheus
|
||||
and blackbox to automatically probe for availability.
|
||||
In this tutorial, we'll go a step further by integrating the [httpbin] service
|
||||
with Prometheus and Blackbox to automatically probe for availability.
|
||||
|
||||
We'll explore how Holos manages [kustomize] bases similar to the Helm kind
|
||||
We'll also explore how Holos manages [kustomize] bases, similar to the Helm kind
|
||||
covered in the [Helm Values] tutorial.
|
||||
|
||||
## The Code
|
||||
@@ -34,8 +34,10 @@ Otherwise click the **Generate** tab to generate a blank platform now.
|
||||
:::
|
||||
</TabItem>
|
||||
<TabItem value="generate" label="Generate">
|
||||
Use `holos` to generate a minimal platform directory structure. First, create
|
||||
and cd into a blank directory. Then use the `holos init platform` command.
|
||||
|
||||
Use `holos` to generate a minimal platform directory structure. First, create
|
||||
and navigate into a blank directory. Then, run the `holos init platform`
|
||||
command.
|
||||
|
||||
```shell
|
||||
mkdir holos-kustomize-tutorial
|
||||
@@ -54,8 +56,8 @@ git init . && git add . && git commit -m initial
|
||||
|
||||
### Managing the Component
|
||||
|
||||
Create the `httpbin` component directory and add the `httpbin.cue` and
|
||||
`httpbin.yaml` files to it.
|
||||
Create the `httpbin` component directory, and add the `httpbin.cue` and
|
||||
`httpbin.yaml` files to it for configuration and setup.
|
||||
|
||||
<Tabs groupId="800C3AE7-E7F8-4AFC-ABF1-6AFECD945958">
|
||||
<TabItem value="setup" label="Setup">
|
||||
@@ -154,9 +156,9 @@ EOF
|
||||
Holos knows the `httpbin.yaml` file is part of the BuildPlan because of the
|
||||
`KustomizeConfig: Files: "httpbin.yaml": _` line in the `httpbin.cue`.
|
||||
|
||||
### Integrating the Components
|
||||
### Register the Components
|
||||
|
||||
Integrate `httpbin` with the platform by adding the following file to the
|
||||
Register `httpbin` with the platform by adding the following file to the
|
||||
platform directory.
|
||||
|
||||
```bash
|
||||
@@ -181,7 +183,7 @@ Render the platform.
|
||||
<Tabs groupId="B120D5D1-0EAB-41E0-AD21-15526EBDD53D">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform ./platform
|
||||
holos render platform
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
@@ -212,10 +214,10 @@ git add . && git commit -m 'add httpbin'
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Inspecting the BuildPlan
|
||||
### Inspecting the Build Plan
|
||||
|
||||
We can see the [BuildPlan] exported to `holos` by the `holos:
|
||||
Kustomize.BuildPlan` line in `httpbin.cue`. Holos processes this build plan to
|
||||
Kustomize.BuildPlan` line in `httpbin.cue`. Holos processes this build plan to
|
||||
produce the fully rendered manifests.
|
||||
|
||||
<Tabs groupId="DD697D65-5BEC-4B92-BB33-59BE4FEC112F">
|
||||
@@ -270,19 +272,21 @@ source:
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Transforming manifests
|
||||
### Transforming Manifests
|
||||
|
||||
Reviewing the BuildPlan exported in the previous command:
|
||||
Review the BuildPlan exported in the previous command:
|
||||
|
||||
1. The [File Generator] copies the plain `httpbin.yaml` file into the build.
|
||||
2. The [Kustomize Transformer] uses `httpbin.yaml` as an input resource.
|
||||
3. The final artifact is the output from Kustomize.
|
||||
|
||||
This BuildPlan transforms the raw yaml by labeling all of the resources with
|
||||
This BuildPlan transforms the raw YAML by labeling all of the resources with
|
||||
`"app.kubernetes.io/name": "httpbin"` using the [KustomizeConfig] `CommonLabels`
|
||||
field. We still need to integrate `httpbin` with `prometheus`. Annotate the
|
||||
Service with `prometheus.io/probe: "true"` to complete the integration. Holos
|
||||
makes this easier with CUE. We don't need to edit any yaml files.
|
||||
field.
|
||||
|
||||
To complete the integration with Prometheus, annotate the Service with
|
||||
`prometheus.io/probe: "true"`. Holos makes this easier with CUE, so there's no
|
||||
need to edit any YAML files manually.
|
||||
|
||||
Add a new `patches.cue` file to the `httpbin` component with the following
|
||||
content.
|
||||
@@ -324,7 +328,7 @@ Render the platform to see the result of the kustomization patch.
|
||||
<Tabs groupId="5D1812DD-8E7B-4F97-B349-275214F38B6E">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform ./platform
|
||||
holos render platform
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
@@ -382,17 +386,19 @@ git add . && git commit -m 'annotate httpbin for prometheus probes'
|
||||
|
||||
## Trying Locally
|
||||
|
||||
Optionally apply the manifests Holos rendered to a [Local Cluster].
|
||||
Optionally, apply the manifests rendered by Holos to a [Local Cluster] for
|
||||
testing and validation.
|
||||
|
||||
## Next Steps
|
||||
|
||||
We learned how Holos makes it easier to manage [httpbin], distributed as a
|
||||
Kustomize base, using a kustomize Component similar to the helm component we saw
|
||||
previously. Holos offers a clear way to kustomize any component, patching an
|
||||
annotation onto the `httpbin` Service in this example.
|
||||
In this tutorial, we learned how Holos simplifies managing [httpbin], which is
|
||||
distributed as a Kustomize base. We used a Kustomize component similar to the
|
||||
Helm component covered previously. Holos provides a straightforward way to
|
||||
customize any component, demonstrated by patching an annotation onto the
|
||||
`httpbin` Service.
|
||||
|
||||
Continue on with the tutorial to explore how Holos makes it easier to manage
|
||||
certificates and make services accessible outside of a cluster.
|
||||
Continue with the tutorial to learn how Holos facilitates certificate management
|
||||
and makes services accessible outside of a cluster.
|
||||
|
||||
[httpbin]: https://github.com/mccutchen/go-httpbin/tree/v2.15.0
|
||||
[prometheus]: https://github.com/prometheus-community/helm-charts/tree/prometheus-25.27.0/charts/prometheus
|
||||
|
||||
@@ -6,68 +6,71 @@ sidebar_position: 10
|
||||
---
|
||||
|
||||
import RenderingOverview from '@site/src/diagrams/rendering-overview.mdx';
|
||||
import YouTube from '@site/src/components/YouTube';
|
||||
|
||||
# Tutorial
|
||||
|
||||
## Overview
|
||||
|
||||
Holos is a configuration management tool for Kubernetes resources. It provides
|
||||
the building blocks needed for implementing the [rendered manifests pattern].
|
||||
It gives the flexibility to manage a wide range of configurations, from large
|
||||
software delivery platforms spanning multiple clusters and regions to generating
|
||||
a single resource on your local device.
|
||||
Holos is a configuration management tool for Kubernetes implementing the
|
||||
[rendered manifests pattern]. It handles configurations ranging from single
|
||||
resources to multi-cluster platforms across regions.
|
||||
|
||||
{/* truncate */}
|
||||
|
||||
At a high level, Holos provides a few major components:
|
||||
|
||||
- A Platform schema for specifying how components integrate together into a platform.
|
||||
- Component building blocks for Helm, Kustomize, and Kubernetes to unify configuration with CUE.
|
||||
- A BuildPlan orchestrating generators, transformers, and validators to produce manifest files.
|
||||
Key components:
|
||||
- Platform schemas defining component integration
|
||||
- Building blocks unifying Helm, Kustomize and Kubernetes configs with CUE
|
||||
- BuildPlan pipeline for generating, transforming and validating manifests
|
||||
|
||||
<RenderingOverview />
|
||||
|
||||
{/* TODO: Replace this with the Advantages diagram we talked about. */}
|
||||
|
||||
## Video
|
||||
|
||||
The video below offers a basic overview of Holos by walking you through the
|
||||
tooling gap at the Kubernetes integration layer and demonstrating how to
|
||||
integrate multiple Helm charts using data from CUE.
|
||||
|
||||
{/* cspell:disable-next-line */}
|
||||
<YouTube id="PSdceGlhHGo"/>
|
||||
|
||||
## Holos' role in your organization
|
||||
|
||||
Platform engineers run the `holos render platform` command locally and in CI to
|
||||
produce Kubernetes manifests which are committed to version control. GitOps
|
||||
tools like ArgoCD or Flux deploy the manifests produced by Holos.
|
||||
Platform engineers use Holos to generate Kubernetes manifests, both locally and
|
||||
in CI pipelines. The manifests are committed to version control and deployed via
|
||||
GitOps tools like ArgoCD or Flux.
|
||||
|
||||
Holos works well with your existing Helm charts, kustomize bases, and any other
|
||||
configuration data you currently store in version control.
|
||||
Holos integrates seamlessly with existing Helm charts, Kustomize bases, and
|
||||
other version-controlled configurations.
|
||||
|
||||
## Advantages of Holos
|
||||
|
||||
### Safe
|
||||
|
||||
Holos uses [CUE] to provide strong typing and constraints to configuration data.
|
||||
Additionally, holos adds strong validation to verify the output produced by Helm
|
||||
and other tools.
|
||||
Holos leverages [CUE] for strong typing and validation of configuration data,
|
||||
ensuring consistent output from Helm and other tools.
|
||||
|
||||
### Consistent
|
||||
|
||||
Holos offers a consistent way to incorporate a wide variety of tools into a well
|
||||
defined data pipeline. Configuration produced from CUE, Helm, and Kustomize are
|
||||
all handled with the same consistent process.
|
||||
A unified pipeline processes all configurations - whether from CUE, Helm, or
|
||||
Kustomize - through the same well-defined stages.
|
||||
|
||||
### Flexible
|
||||
|
||||
Holos is designed to be flexible. Holos offers flexible building blocks for
|
||||
data generation, transformation, validation, and integration. Find the perfect
|
||||
fit for your team by assembling these building blocks to your unique needs.
|
||||
Composable building blocks for generation, transformation, validation and
|
||||
integration let teams assemble workflows that match their needs.
|
||||
|
||||
Holos does not have an opinion on many common aspects of platform configuration.
|
||||
For example, environments and clusters are explicitly kept out of the core and
|
||||
instead are provided as flexible, user-customizable [topics] organized as a
|
||||
recipes.
|
||||
The core is intentionally unopinionated about platform configuration patterns.
|
||||
Common needs like environments and clusters are provided as customizable
|
||||
[topics] recipes rather than enforced structures.
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you get stuck, you can get help on [Discord] or [GitHub discussions]. Don't
|
||||
worry about asking "beginner" questions, configuration is often complex even for
|
||||
the most experienced among us. We all start somewhere and are happy to help.
|
||||
Get support through our [Discord] channel or [GitHub discussions]. Configuration
|
||||
challenges arise at all experience levels - we welcome your questions and are
|
||||
here to help.
|
||||
|
||||
[rendered manifests pattern]: https://akuity.io/blog/the-rendered-manifests-pattern
|
||||
[CUE]: https://cuelang.org/
|
||||
|
||||
@@ -11,22 +11,23 @@ import RenderPlatformDiagram from '@site/src/diagrams/render-platform-sequence.m
|
||||
|
||||
# Setup
|
||||
|
||||
## Overview
|
||||
|
||||
This tutorial will guide you through the installation of Holos and its
|
||||
dependencies, as well as the initialization of a minimal Platform that you can
|
||||
extend to meet your specific needs.
|
||||
|
||||
## Installing
|
||||
|
||||
Holos is distributed as a single file executable that can be installed in a
|
||||
couple of ways.
|
||||
Holos is a single executable that can be installed via:
|
||||
|
||||
<Tabs groupId="FE2C74C8-B3A3-4AEA-BBD3-F57FAA654B6F">
|
||||
<TabItem value="brew" label="Install with brew">
|
||||
<TabItem value="brew" label="macOS">
|
||||
```bash
|
||||
brew install holos-run/tap/holos
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="linux" label="Linux">
|
||||
Download holos from the [releases] page and place the executable into your shell
|
||||
path.
|
||||
</TabItem>
|
||||
<TabItem value="win" label="Windows">
|
||||
Download holos from the [releases] page and place the executable into your shell
|
||||
path.
|
||||
</TabItem>
|
||||
<TabItem value="go" label="Go">
|
||||
```bash
|
||||
@@ -37,8 +38,23 @@ go install github.com/holos-run/holos/cmd/holos@latest
|
||||
|
||||
### Completion
|
||||
|
||||
:::tip
|
||||
Completion is automatically enabled if [brew shell
|
||||
completion](https://docs.brew.sh/Shell-Completion) is also enabled.
|
||||
:::
|
||||
|
||||
<Tabs groupId="65F79D28-2E57-4A90-8EBA-3D8758C80233">
|
||||
<TabItem value="zsh" label="zsh">
|
||||
|
||||
Add the following to `~/.zshrc` if not already present to initialize zsh completion.
|
||||
|
||||
```bash
|
||||
autoload -Uz compinit
|
||||
compinit
|
||||
```
|
||||
|
||||
Then load holos completion after zsh completion has been initialized.
|
||||
|
||||
```bash
|
||||
source <(holos completion zsh)
|
||||
```
|
||||
@@ -60,31 +76,22 @@ holos completion powershell | Invoke-Expression
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Releases
|
||||
|
||||
Download `holos` from the [releases] page and place the executable into your
|
||||
shell path.
|
||||
|
||||
### Dependencies
|
||||
|
||||
Holos integrates with the following tools that should be installed to enable
|
||||
their functionality.
|
||||
Install these tools to use Holos's full capabilities:
|
||||
|
||||
- [Helm] to fetch and render Helm chart Components.
|
||||
- [Kubectl] to [kustomize] components.
|
||||
- [Helm] for chart management and rendering
|
||||
- [Kubectl] for [kustomize] operations
|
||||
|
||||
:::note
|
||||
Holos is tested with Helm version `v3.16.2`.
|
||||
Holos is tested with Helm `v3.16.2`. If you see `Error: chart requires
|
||||
kubeVersion` errors, try upgrading Helm.
|
||||
:::
|
||||
|
||||
Please try upgrading helm if you encounter `Error: chart requires kubeVersion
|
||||
...` errors.
|
||||
|
||||
## Next Steps
|
||||
|
||||
You've got the structure of your platform configuration in place. Continue on to
|
||||
[Hello Holos] where you'll learn how easy it is to manage a Helm chart with
|
||||
holos.
|
||||
With your platform structure initialized, proceed to [Hello Holos] to learn Helm
|
||||
chart management.
|
||||
|
||||
[Helm]: https://github.com/helm/helm/releases
|
||||
[Kubectl]: https://kubernetes.io/docs/tasks/tools/
|
||||
|
||||
428
doc/md/tutorial/validators.mdx
Normal file
428
doc/md/tutorial/validators.mdx
Normal file
@@ -0,0 +1,428 @@
|
||||
---
|
||||
slug: validators
|
||||
title: Validators
|
||||
description: Validate rendered manifests against policy definitions.
|
||||
sidebar_position: 60
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import RenderingOverview from '@site/src/diagrams/rendering-overview.mdx';
|
||||
|
||||
# Validators
|
||||
|
||||
## Overview
|
||||
|
||||
Sometimes Helm charts render Secrets we do not wanted committed to version
|
||||
control for security. Helm charts often render incorrect manifests, even if
|
||||
they're accepted by the api server. For example, passing `null` to collection
|
||||
fields. We'll solve both of these issues using a [Validator] to block artifacts
|
||||
with a Secret resource, and verifying the artifact against Kubernetes type
|
||||
definitions.
|
||||
|
||||
1. If a Helm chart renders a Secret, Holos errors before writing the artifact
|
||||
and suggests an ExternalSecret instead.
|
||||
2. Each resource is validated against a field named by the value of the kind
|
||||
field. For example, a `kind: Secret` resource validates against `secret: {}` in
|
||||
CUE. `kind: Deployment` validates against `deployment: {}` in CUE.
|
||||
3. The final artifact is validated, covering the output of all generators and
|
||||
transformers.
|
||||
|
||||
<RenderingOverview />
|
||||
|
||||
## The Code
|
||||
|
||||
### Generating the Structure
|
||||
|
||||
Use `holos` to generate a minimal platform directory structure. First, create
|
||||
and navigate into a blank directory. Then, use the `holos generate platform`
|
||||
command to generate a minimal platform.
|
||||
|
||||
```shell
|
||||
mkdir holos-validators-tutorial && cd holos-validators-tutorial
|
||||
holos init platform v1alpha5
|
||||
```
|
||||
|
||||
### Creating the Component
|
||||
|
||||
Create the directory for the `podinfo` component. Create an empty file, then add
|
||||
the following CUE configuration to it.
|
||||
|
||||
```bash
|
||||
mkdir -p components/podinfo
|
||||
```
|
||||
```bash
|
||||
cat <<EOF > components/podinfo/podinfo.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
|
||||
// export the component build plan to holos
|
||||
holos: Component.BuildPlan
|
||||
|
||||
// Component is a Helm chart
|
||||
Component: #Helm & {
|
||||
Name: "podinfo"
|
||||
Namespace: "default"
|
||||
// Add metadata.namespace to all resources with kustomize.
|
||||
KustomizeConfig: Kustomization: namespace: Namespace
|
||||
Chart: {
|
||||
version: "6.6.2"
|
||||
repository: {
|
||||
name: "podinfo"
|
||||
url: "https://stefanprodan.github.io/podinfo"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```bash
|
||||
EOF
|
||||
```
|
||||
|
||||
Register the component with the platform.
|
||||
|
||||
```bash
|
||||
cat <<EOF > platform/podinfo.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
|
||||
Platform: Components: podinfo: {
|
||||
name: "podinfo"
|
||||
path: "components/podinfo"
|
||||
}
|
||||
```
|
||||
```bash
|
||||
EOF
|
||||
```
|
||||
|
||||
Render the platform.
|
||||
|
||||
<Tabs groupId="tutorial-hello-render-manifests">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```
|
||||
cached podinfo 6.6.2
|
||||
rendered podinfo in 1.938665041s
|
||||
rendered platform in 1.938759417s
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Add and commit the initial configuration.
|
||||
|
||||
```bash
|
||||
git init . && git add . && git commit -m initial
|
||||
```
|
||||
|
||||
### Define the Valid Schema
|
||||
|
||||
We'll use a CUE package named `policy` so the entire platform configuration in
|
||||
package `holos` isn't loaded every time we validate an artifact.
|
||||
|
||||
Create `policy/validation-schema.cue` with the following content.
|
||||
|
||||
```shell
|
||||
mkdir -p policy
|
||||
cat <<EOF > policy/validation-schema.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package policy
|
||||
|
||||
import apps "k8s.io/api/apps/v1"
|
||||
|
||||
// Organize by kind then name to avoid conflicts.
|
||||
kind: [KIND=string]: [NAME=string]: {...}
|
||||
|
||||
// Useful when one component manages the same resource kind and name across
|
||||
// multiple namespaces.
|
||||
let KIND = kind
|
||||
namespace: [NS=string]: KIND
|
||||
|
||||
// Block Secret resources. kind will not unify with "Secret"
|
||||
kind: secret: [NAME=string]: kind: "Use an ExternalSecret instead. Forbidden by security policy. secret/\(NAME)"
|
||||
|
||||
// Validate Deployment against Kubernetes type definitions.
|
||||
kind: deployment: [_]: apps.#Deployment
|
||||
```
|
||||
```shell
|
||||
EOF
|
||||
```
|
||||
|
||||
### Configuring Validators
|
||||
|
||||
Configure the Validators [ComponentConfig] field to configure each [BuildPlan]
|
||||
to validate the rendered [Artifact] files.
|
||||
|
||||
```shell
|
||||
cat <<EOF > validators.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
|
||||
// Configure all component kinds to validate against the policy directory.
|
||||
#ComponentConfig: Validators: cue: {
|
||||
kind: "Command"
|
||||
// Note --path maps each resource to a top level field named by the kind.
|
||||
command: args: [
|
||||
"holos",
|
||||
"cue",
|
||||
"vet",
|
||||
"./policy",
|
||||
"--path=\"namespace\"",
|
||||
"--path=metadata.namespace",
|
||||
"--path=strings.ToLower(kind)",
|
||||
"--path=metadata.name",
|
||||
]
|
||||
}
|
||||
```
|
||||
```shell
|
||||
EOF
|
||||
```
|
||||
|
||||
### Patching Errors
|
||||
|
||||
Render the platform to see validation fail. The podinfo chart has no Secret,
|
||||
but it produces an invalid Deployment because it sets the container resource
|
||||
limits field to `null`.
|
||||
|
||||
```shell
|
||||
holos render platform
|
||||
```
|
||||
|
||||
```txt
|
||||
deployment.spec.template.spec.containers.0.resources.limits: conflicting values null and {[string]:"k8s.io/apimachinery/pkg/api/resource".#Quantity} (mismatched types null and struct):
|
||||
./cue.mod/gen/k8s.io/api/apps/v1/types_go_gen.cue:355:9
|
||||
./cue.mod/gen/k8s.io/api/apps/v1/types_go_gen.cue:376:12
|
||||
./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:2840:11
|
||||
./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:2968:14
|
||||
./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:3882:15
|
||||
./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:3882:18
|
||||
./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:5027:9
|
||||
./cue.mod/gen/k8s.io/api/core/v1/types_go_gen.cue:6407:16
|
||||
./policy/validation-schema.cue:9:13
|
||||
../../../../../var/folders/22/T/holos.validate1636392304/components/podinfo/podinfo.gen.yaml:104:19
|
||||
could not run: terminating because of errors
|
||||
could not run: could not validate podinfo path ./components/podinfo: could not run command: holos cue vet ./policy --path strings.ToLower(kind) /var/folders/22/T/holos.validate1636392304/components/podinfo/podinfo.gen.yaml: exit status 1 at builder/v1alpha5/builder.go:411
|
||||
could not run: could not render component: could not run command: holos --log-level info --log-format console render component --inject holos_component_name=podinfo --inject holos_component_path=components/podinfo ./components/podinfo: exit status 1 at cli/render/render.go:155
|
||||
```
|
||||
|
||||
We'll use a [Kustomize] patch [Transformer] to replace the `null` limits field
|
||||
with a valid equivalent value.
|
||||
|
||||
:::important
|
||||
This configuration is defined in CUE, not YAML, even though we're configuring a
|
||||
Kustomize patch transformer. CUE gives us access to the unified platform
|
||||
configuration.
|
||||
:::
|
||||
|
||||
```shell
|
||||
cat <<EOF > components/podinfo/patch.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
|
||||
import "encoding/yaml"
|
||||
|
||||
Component: KustomizeConfig: Kustomization: {
|
||||
_patches: limits: {
|
||||
target: kind: "Deployment"
|
||||
patch: yaml.Marshal([{
|
||||
op: "test"
|
||||
path: "/spec/template/spec/containers/0/resources/limits"
|
||||
value: null
|
||||
}, {
|
||||
op: "replace"
|
||||
path: "/spec/template/spec/containers/0/resources/limits"
|
||||
value: {}
|
||||
}])
|
||||
}
|
||||
patches: [for x in _patches {x}]
|
||||
}
|
||||
```
|
||||
```shell
|
||||
EOF
|
||||
```
|
||||
|
||||
Now the platform renders.
|
||||
|
||||
<Tabs groupId="3A050092-8E56-49D4-84A9-71E544A21276">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos render platform
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="output" label="Output">
|
||||
```txt
|
||||
rendered podinfo in 181.875083ms
|
||||
rendered platform in 181.975833ms
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Inspecting the BuildPlan
|
||||
|
||||
The BuildPlan patches the output of the upstream helm chart without modifying
|
||||
it, then validates the artifact against the Kubernetes type definitions.
|
||||
|
||||
<Tabs groupId="1DAB4C46-0793-4CCA-8930-7B2E60BDA1BE">
|
||||
<TabItem value="command" label="Command">
|
||||
```bash
|
||||
holos show buildplans
|
||||
```
|
||||
</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: {}
|
||||
namespace: default
|
||||
- kind: Resources
|
||||
output: resources.gen.yaml
|
||||
transformers:
|
||||
- kind: Kustomize
|
||||
inputs:
|
||||
- helm.gen.yaml
|
||||
- resources.gen.yaml
|
||||
output: components/podinfo/podinfo.gen.yaml
|
||||
kustomize:
|
||||
kustomization:
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: default
|
||||
patches:
|
||||
- patch: |
|
||||
- op: test
|
||||
path: /spec/template/spec/containers/0/resources/limits
|
||||
value: null
|
||||
- op: replace
|
||||
path: /spec/template/spec/containers/0/resources/limits
|
||||
value: {}
|
||||
target:
|
||||
kind: Deployment
|
||||
name: ""
|
||||
resources:
|
||||
- helm.gen.yaml
|
||||
- resources.gen.yaml
|
||||
validators:
|
||||
- kind: Command
|
||||
inputs:
|
||||
- components/podinfo/podinfo.gen.yaml
|
||||
command:
|
||||
args:
|
||||
- holos
|
||||
- cue
|
||||
- vet
|
||||
- ./policy
|
||||
- --path
|
||||
- strings.ToLower(kind)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Catching Mistakes
|
||||
|
||||
Suppose a teammate downloads a helm chart that includes a Secret unbeknown to
|
||||
them. Holos catches the problem and suggests an ExternalSecret instead.
|
||||
|
||||
Mix in a Secret to see what happens
|
||||
|
||||
```shell
|
||||
cat <<EOF > components/podinfo/secret.cue
|
||||
```
|
||||
```cue showLineNumbers
|
||||
package holos
|
||||
|
||||
Component: Resources: Secret: example: metadata: name: "example"
|
||||
```
|
||||
```shell
|
||||
EOF
|
||||
```
|
||||
|
||||
Render the platform to see the error.
|
||||
|
||||
```shell
|
||||
holos render platform
|
||||
```
|
||||
```txt
|
||||
secret.kind: conflicting values "Use an ExternalSecret instead. Forbidden by security policy." and "Secret":
|
||||
./policy/validation-schema.cue:6:15
|
||||
../../../../../var/folders/22/T/holos.validate2549739170/components/podinfo/podinfo.gen.yaml:1:7
|
||||
could not run: terminating because of errors
|
||||
could not run: could not validate podinfo path ./components/podinfo: could not run command: holos cue vet ./policy --path strings.ToLower(kind) /var/folders/22/T/holos.validate2549739170/components/podinfo/podinfo.gen.yaml: exit status 1 at builder/v1alpha5/builder.go:411
|
||||
could not run: could not render component: could not run command: holos --log-level info --log-format console render component --inject holos_component_name=podinfo --inject holos_component_path=components/podinfo ./components/podinfo: exit status 1 at cli/render/render.go:155
|
||||
```
|
||||
|
||||
:::important
|
||||
Holos quickly returns an error if validated artifacts have a Secret.
|
||||
:::
|
||||
|
||||
Remove the secret to resolve the issue.
|
||||
|
||||
```shell
|
||||
rm components/podinfo/secret.cue
|
||||
```
|
||||
|
||||
## Inspecting the diff
|
||||
|
||||
The validation and patch results in a correct Deployment, verified against the
|
||||
Kubernetes type definitions.
|
||||
|
||||
```shell
|
||||
git diff
|
||||
```
|
||||
```diff
|
||||
diff --git a/deploy/components/podinfo/podinfo.gen.yaml b/deploy/components/podinfo/podinfo.gen.yaml
|
||||
index 6e4aec0..a145e3f 100644
|
||||
--- a/deploy/components/podinfo/podinfo.gen.yaml
|
||||
+++ b/deploy/components/podinfo/podinfo.gen.yaml
|
||||
@@ -101,7 +101,7 @@ spec:
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 5
|
||||
resources:
|
||||
- limits: null
|
||||
+ limits: {}
|
||||
requests:
|
||||
cpu: 1m
|
||||
memory: 16Mi
|
||||
```
|
||||
|
||||
## Trying Locally
|
||||
|
||||
Optionally, apply the manifests rendered by Holos to a [Local Cluster] for
|
||||
testing.
|
||||
|
||||
[Local Cluster]: ../topics/local-cluster.mdx
|
||||
[ExternalSecret]: https://external-secrets.io/latest/api/externalsecret/
|
||||
[Artifact]: ../api/core.md#Artifact
|
||||
[BuildPlan]: ../api/core.md#BuildPlan
|
||||
[Resources]: ../api/core.md#Resources
|
||||
[Validator]: ../api/core.md#Validator
|
||||
[Transformer]: ../api/core.md#Transformer
|
||||
[Kustomize]: ../api/core.md#Kustomize
|
||||
[Generator]: ../api/core.md#Generator
|
||||
[Hello Holos]: ./hello-holos.mdx
|
||||
[cue.mod/gen/external-secrets.io/externalsecret/v1beta1/types_gen.cue]: https://github.com/holos-run/holos/blob/main/internal/generate/platforms/cue.mod/gen/external-secrets.io/externalsecret/v1beta1/types_gen.cue#L13
|
||||
[ComponentConfig]: ../api/author.md#ComponentConfig
|
||||
[timoni]: https://timoni.sh/install/
|
||||
[resources.cue]: https://github.com/holos-run/holos/blob/main/internal/generate/platforms/v1alpha5/resources.cue#L33
|
||||
4
doc/website/.gitignore
vendored
4
doc/website/.gitignore
vendored
@@ -18,3 +18,7 @@
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Auto-generated doc examples
|
||||
multi-sources-example/
|
||||
kargo-demo/
|
||||
|
||||
35
doc/website/blog/2024-11-25-validators.mdx
Normal file
35
doc/website/blog/2024-11-25-validators.mdx
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
slug: validators-feature
|
||||
title: Validators added in Holos v0.101.0
|
||||
authors: [jeff]
|
||||
tags: [holos, feature]
|
||||
image: /img/cards/validators.png
|
||||
description: Validators are useful to enforce policy and catch Helm errors.
|
||||
---
|
||||
|
||||
import RenderingOverview from '@site/src/diagrams/rendering-overview.mdx';
|
||||
import RenderPlatformDiagram from '@site/src/diagrams/render-platform-sequence.mdx';
|
||||
|
||||
We've added support for [Validators] in [v0.101.0]. Validators are useful to
|
||||
enforce policies and ensure consistency early in the process. This feature
|
||||
addresses two primary use cases:
|
||||
|
||||
1. Prevent insecure configuration early in the process. For example, prevent
|
||||
Helm from rendering a `Secret` which would otherwise be committed to version control.
|
||||
2. Prevent unsafe configuration by validating manifests against Kubernetes core
|
||||
and custom resource type definitions.
|
||||
|
||||
Check out the [Validators] tutorial for examples of both use cases.
|
||||
|
||||
[Validators]: https://holos.run/docs/v1alpha5/tutorial/validators/
|
||||
[v0.101.0]: https://github.com/holos-run/holos/releases/tag/v0.101.0
|
||||
|
||||
{/* truncate */}
|
||||
|
||||
<RenderingOverview />
|
||||
|
||||
Validators complete the core functionality of the Holos manifest rendering
|
||||
pipeline. We are seeking design partners to help enhance Generators and
|
||||
Transformers. Validators are implemented using a generic `Command` kind, and
|
||||
we're considering a similar kind for Generators and Transformers. Please
|
||||
connect with us if you'd like to help design these enhancements.
|
||||
@@ -0,0 +1,627 @@
|
||||
---
|
||||
unlisted: true
|
||||
slug: trade-in-argocd-appsets-for-rendered-manifests
|
||||
title: Trade in the complexity of ArgoCD AppSets for fully rendered manifests
|
||||
authors: [jeff]
|
||||
tags: [holos, helm, gitops]
|
||||
# image: /img/cards/validators.png
|
||||
description: Migrate an ApplicationSet to the rendered manifest pattern with Holos, removing unnecessary complexity.
|
||||
keywords:
|
||||
- Holos
|
||||
- CUE
|
||||
- Configuration
|
||||
- Structure
|
||||
- Kubernetes
|
||||
- Hydrated
|
||||
- Rendered
|
||||
- Manifest
|
||||
- Pattern
|
||||
- Unification
|
||||
- ArgoCD
|
||||
- ApplicationSet
|
||||
- Application
|
||||
- Multi Source
|
||||
- Values Hierarchy
|
||||
- Rendered Manifest Pattern
|
||||
- GitOps
|
||||
- Complexity
|
||||
---
|
||||
|
||||
Kubernetes has a reputation for being too complex. Complexity in software
|
||||
engineering is often categorized as essential or accidental. Methods to expose
|
||||
services to the internet, deploy replicas, manage secrets, configure network
|
||||
connectivity, and control access are examples of essential complexity. They're
|
||||
unavoidable no matter what tools we use.
|
||||
|
||||
Nevertheless, popular tools in the ecosystem bolster this reputation by
|
||||
accidentally piling up layers of complexity unnecessarily. For example, Helm
|
||||
value override hierarchies and ArgoCD ApplicationSet templates. Both tools
|
||||
solve challenging, pervasive problems resulting in their widespread adoption.
|
||||
Unfortunately the trade offs each one makes independently combine together
|
||||
poorly. Helm and ArgoCD ApplicationSets contribute more than their fair share
|
||||
of accidental complexity to the Kubernetes ecosystem.
|
||||
|
||||
Consider the use case of deploying different versions of the same service to
|
||||
multiple environments. An ArgoCD ApplicationSet passing sets of values to one
|
||||
Helm chart is a commonly recommended solution contributing three forms of
|
||||
accidental complexity.
|
||||
|
||||
1. There are multiple layers of Go template abstractions.
|
||||
2. Config values are silently written over at multiple layers in a hierarchy.
|
||||
3. The intermediate and final configuration is remote and out of reach.
|
||||
|
||||
The rendered manifests pattern is an alternative solution leveraging Helm and
|
||||
ArgoCD with less complexity. The pattern reduces complexity by collapsing
|
||||
multiple layers of Go templates and brings the configuration local, within our
|
||||
reach. It still relies on Helm value hierarchies when charts are reused across
|
||||
environments, so it's no silver bullet, but we'll explain in follow up post how
|
||||
Holos can eliminate the accidental complexity of a Helm value files hierarchy.
|
||||
|
||||
There is no widely agreed upon, freely available implementation of the rendered
|
||||
manifest pattern. Engineering teams have to decide whether to implement the
|
||||
pattern from scratch or stick with the more complex, but built-in features of
|
||||
Helm and ArgoCD. These few options indicate a tooling gap in the ecosystem.
|
||||
This gap pushes many organizations toward the accidental complexity of the
|
||||
ApplicationSet solution.
|
||||
|
||||
Holos fills this gap by offering a thoughtful and complete implementation of the
|
||||
rendered manifest pattern in one command line tool. This article is the first
|
||||
in a series exploring how Holos solves the same use case while avoiding the
|
||||
accidental complexity.
|
||||
|
||||
This article walks step-by-step through the process of migrating an
|
||||
ApplicationSet to Holos. I'll explain why we feel the trades we made in Holos
|
||||
are a net improvement. At the end of the article you'll see how you can
|
||||
continue leveraging GitOps with ArgoCD and Helm while gaining the ability to see
|
||||
and comprehend complex configurations clearly, with fewer layers of abstraction.
|
||||
|
||||
{/* truncate */}
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
## Layers of Complexity
|
||||
|
||||
Before diving into the step by step migration, let's explore exactly what we
|
||||
mean by accidental complexity. Consider the ApplicationSet and Helm value
|
||||
hierarchy described in the final recommendation of _[Using Helm Hierarchies in
|
||||
Multi-Source Argo CD Applications for Promoting to Different GitOps
|
||||
Environments][tfa]_. This particular ApplicationSet is a prime example because
|
||||
the approach it takes is often recommended as a solution for deploying a service
|
||||
to multiple clusters and environments.
|
||||
|
||||
Unfortunately the example provided requires us to hold _at least 7 layers of
|
||||
abstraction_ in mind while considering the impact of a change.
|
||||
|
||||
- The ApplicationSet renders an Application template for each `config.json` file.
|
||||
- Each Application `valueFiles` field introduces 5 more layers of potential overrides.
|
||||
- The Helm Chart is another layer of text templates.
|
||||
|
||||
:::important
|
||||
Consider the multiple layers of abstraction in the example provided compared
|
||||
with their replacements shown in the [Goals](#goals) section.
|
||||
:::
|
||||
|
||||
import AppSetPath from '!!raw-loader!./_migrate_appset/script-02-layers-of-complexity/appset.path';
|
||||
import AppSetYAML from '!!raw-loader!./_migrate_appset/script-02-layers-of-complexity/appset.yaml';
|
||||
import DeploymentPath from '!!raw-loader!./_migrate_appset/script-02-layers-of-complexity/deployment.path';
|
||||
import DeploymentYAML from '!!raw-loader!./_migrate_appset/script-02-layers-of-complexity/deployment.yaml';
|
||||
|
||||
<Tabs groupId="layers-of-complexity">
|
||||
<TabItem value="Application Template" label="Application Template">
|
||||
The ApplicationSet template renders a Helm template.
|
||||
<CodeBlock language="txt">{AppSetPath}</CodeBlock>
|
||||
<CodeBlock language="yaml">{AppSetYAML}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Deployment Template" label="Deployment Template">
|
||||
The Helm template renders the final Deployment configuration.
|
||||
<CodeBlock language="txt">{DeploymentPath}</CodeBlock>
|
||||
<CodeBlock language="yaml">{DeploymentYAML}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
These templates would be easier to comprehend if their intermediate state and
|
||||
final configuration were within reach. Unfortunately, it's stored in multiple
|
||||
remote repositories and processed remotely in ArgoCD. ArgoCD is like a black
|
||||
box. Intermediate state is inaccessible. The final configuration is only
|
||||
accessible after it's been applied, far too late to consider what impact a
|
||||
change might have.
|
||||
|
||||
We know the complexity is accidental because we bypass these layers when we run
|
||||
`kubectl edit`. The configuration is fetched, manipulated locally, then pushed
|
||||
back to the cluster without obfuscation.
|
||||
|
||||
We also know the complexity is real. It's too easy to make mistakes when
|
||||
configuration values are silently written over many times along the way. For
|
||||
example, we'll see how the very ApplicationSet we're migrating contains errors
|
||||
which are difficult to fix without insight a tool like `holos` provides.
|
||||
|
||||
## Goals
|
||||
|
||||
We want to eliminate as many layers of accidental complexity as possible with as
|
||||
few changes as possible. Ideally we'll be able to directly see and manipulate
|
||||
the final manifests as they will be applied to the cluster. We also want the
|
||||
migration to balance the conventions and idioms of Holos, Helm, and ArgoCD as
|
||||
much as possible.
|
||||
|
||||
1. Eliminate as many layers of accidental complexity as possible.
|
||||
2. Make as few changes as possible.
|
||||
3. Bring intermediate state and the final configuration within our reach.
|
||||
4. Use each tool idiomatically.
|
||||
|
||||
We'll relocate the unmodified Helm chart, config.json files, and value files to
|
||||
make as few changes as possible. We'll also generate Applications for ArgoCD
|
||||
identical to those generated by the ApplicationSet, but we'll do so with the
|
||||
same [CUE] layer that configures everything else in Holos.
|
||||
|
||||
The migration achieves these goals by rendering clearly readable Application and
|
||||
Deployment resources to local files for ArgoCD to sync via GitOps. Here's how
|
||||
it will look.
|
||||
|
||||
:::important
|
||||
Compare the Application and Deployment with the templates in the [Layers of
|
||||
Complexity](#layers-of-complexity) section.
|
||||
:::
|
||||
|
||||
import AppTreeCommand from '!!raw-loader!./_migrate_appset/script-05-application/tree-deploy.sh'
|
||||
import AppTreeOutput from '!!raw-loader!./_migrate_appset/script-05-application/tree-deploy.txt'
|
||||
import AppPath from '!!raw-loader!./_migrate_appset/script-05-application/app.path'
|
||||
import AppYAML from '!!raw-loader!./_migrate_appset/script-05-application/app.yaml'
|
||||
import ManifestPath from '!!raw-loader!./_migrate_appset/script-05-application/manifest.path'
|
||||
import ManifestYAML from '!!raw-loader!./_migrate_appset/script-05-application/manifest.yaml'
|
||||
|
||||
<Tabs groupId="goal-summary">
|
||||
<TabItem value="Application" label="Application">
|
||||
<CodeBlock language="yaml">
|
||||
{"# "+AppPath}
|
||||
{AppYAML}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Deployment" label="Deployment">
|
||||
<CodeBlock language="yaml">
|
||||
{"# "+ManifestPath}
|
||||
{ManifestYAML}
|
||||
</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Tree" label="Tree">
|
||||
<CodeBlock language="bash">{AppTreeCommand}</CodeBlock>
|
||||
<CodeBlock language="txt">{AppTreeOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The manifests rendered at the end of the migration achieve the goals.
|
||||
|
||||
1. The Application rendered by Holos is readable and complete, replacing the ApplicationSet template rendered remotely.
|
||||
2. The Application is equivalent to those produced by the ApplicationSet.
|
||||
3. The final configuration is within reach in local files. We'll see how Holos exposes intermediate state as we step through the migration in the next section.
|
||||
4. Both ArgoCD and Helm are used idiomatically, passing the value files to render the chart for GitOps.
|
||||
|
||||
## Migration Steps
|
||||
|
||||
We'll migrate each of the three major behaviors of the ApplicationSet to Holos
|
||||
to achieve the goals and complete the migration.
|
||||
|
||||
1. Generate an Application from a template using values provided by each `config.json` file.
|
||||
2. Render `my-chart` to a manifest by providing a hierarchy of helm values determined by `config.json` values.
|
||||
3. Reconcile the rendered manifest with the cluster state.
|
||||
|
||||
We'll start by loading the `config.json` environment data files into CUE without
|
||||
modifying the original data. Then we'll manage a Holos [Platform] [Component]
|
||||
for each environment. We'll wrap `my-chart` in a component definition
|
||||
and pass the value hierarchy to `helm template` the same as ArgoCD does.
|
||||
Finally, we'll mix an ArgoCD Application into each platform component to achieve
|
||||
the same output as the ApplicationSet.
|
||||
|
||||
Along the way we'll see how Holos eliminates accidental complexity and makes it
|
||||
easier to work with the intermediate and final configuration.
|
||||
|
||||
### Initial Setup
|
||||
|
||||
The main branch of the [multi-sources-example] is a fork of the example code
|
||||
from the original article that has already been migrated. We'll roll back to
|
||||
the fork point then step through each of the commits to complete the migration.
|
||||
|
||||
First, clone the repository.
|
||||
|
||||
import GitCloneCommand from '!!raw-loader!./_migrate_appset/script-01-clone/clone.sh';
|
||||
import GitCloneOutput from '!!raw-loader!./_migrate_appset/script-01-clone/clone.txt';
|
||||
|
||||
<Tabs groupId="clone">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{GitCloneCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{GitCloneOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Then, reset to where it was forked from upstream.
|
||||
|
||||
import GitResetCommand from '!!raw-loader!./_migrate_appset/script-01-clone/reset.sh';
|
||||
import GitResetOutput from '!!raw-loader!./_migrate_appset/script-01-clone/reset.txt';
|
||||
|
||||
|
||||
<Tabs groupId="reset">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{GitResetCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{GitResetOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
[Install Holos] if you haven't already. This article has been tested with version:
|
||||
|
||||
import HolosVersionCmd from '!!raw-loader!./_migrate_appset/script-01-clone/version.sh';
|
||||
import HolosVersionTxt from '!!raw-loader!./_migrate_appset/script-01-clone/version.txt';
|
||||
|
||||
|
||||
<Tabs groupId="version">
|
||||
<TabItem value="Version" label="Version">
|
||||
<CodeBlock language="txt">{HolosVersionTxt}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{HolosVersionCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Holos Structure
|
||||
|
||||
Holos is organized for use as a GitOps repository. The main folders are:
|
||||
|
||||
| Folder | Description |
|
||||
| - | - |
|
||||
| platform | Entry point for `holos render platform` which renders all manifests. |
|
||||
| config | Configuration data, we'll move the `config.json` files here. |
|
||||
| components | Component definitions live here. Components produce build plans for `holos` to render manifests. We'll wrap `my-chart` in a reusable component definition. |
|
||||
| deploy | Fully rendered manifests are written here for ArgoCD to sync. |
|
||||
| cue.mod | [CUE] type definitions and schemas reside here. |
|
||||
|
||||
|
||||
Initialize the platform.
|
||||
|
||||
import HolosInit from '!!raw-loader!./_migrate_appset/script-03-holos-structure/holos-init.sh';
|
||||
|
||||
<CodeBlock language="bash">{"# --force is necessary when the current directory is not empty\n"+HolosInit}</CodeBlock>
|
||||
|
||||
Now that we've initialized the current directory as a Holos platform repository
|
||||
we can move the example files from the original article into their conventional
|
||||
locations in Holos.
|
||||
|
||||
import MoveFiles from '!!raw-loader!./_migrate_appset/script-03-holos-structure/move-files-around.sh';
|
||||
|
||||
<CodeBlock language="bash">{MoveFiles}</CodeBlock>
|
||||
|
||||
### Environment Configs
|
||||
|
||||
The ApplicationSet generators field iterates over 8 config.json files to
|
||||
instantiate each Application from the spec.template field. We'll migrate this
|
||||
to a similar mechanism in Holos by using CUE's `@embed` feature to load the same
|
||||
files into one struct. We'll manage one Helm Component for each config.json
|
||||
value in the struct. This struct will reside in the `config` field of the
|
||||
`environments` package. Like Go, CUE supports package imports for reuse.
|
||||
|
||||
These `config.json` files moved to the `config/environments/` folder. The
|
||||
`config` folder is the conventional place in Holos for reusable config values
|
||||
like these.
|
||||
|
||||
:::important
|
||||
Holos offers one unified layer with CUE to configure an entire platform
|
||||
holistically, different from other tools like Helm and Kustomize.
|
||||
:::
|
||||
|
||||
Here's how the environments package is defined in CUE.
|
||||
|
||||
import EnvironmentsPackageHeader from '!!raw-loader!./_migrate_appset/script-03-holos-structure/environments-header.sh';
|
||||
import EnvironmentsPackageBody from '!!raw-loader!./_migrate_appset/script-03-holos-structure/environments.cue';
|
||||
import EnvironmentsPackageTrailer from '!!raw-loader!./_migrate_appset/script-03-holos-structure/environments-trailer.sh';
|
||||
|
||||
<CodeBlock language="bash">{EnvironmentsPackageHeader}</CodeBlock>
|
||||
<CodeBlock language="cue" showLineNumbers>{EnvironmentsPackageBody}</CodeBlock>
|
||||
<CodeBlock language="bash">{EnvironmentsPackageTrailer}</CodeBlock>
|
||||
|
||||
We moved the original `config.json` files without modifying them, then used
|
||||
CUE's `@embed` feature to load them into the `config` struct. This structure is
|
||||
accessible in CUE by importing the `environments` package, then referencing
|
||||
`environments.config`.
|
||||
|
||||
:::tip
|
||||
Holos and CUE offer fast, local query and manipulation of your configuration
|
||||
data, even in intermediate states.
|
||||
:::
|
||||
|
||||
Here's how the environments package exports to YAML. `cue export` and `cue
|
||||
eval` are handy ways to query intermediate state.
|
||||
|
||||
import InspectEnvironmentsCommand from '!!raw-loader!./_migrate_appset/script-03-holos-structure/inspect-environments.sh';
|
||||
import InspectEnvironmentsOutput from '!!raw-loader!./_migrate_appset/script-03-holos-structure/inspect-environments.txt';
|
||||
|
||||
<Tabs groupId="inspect-environments">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{InspectEnvironmentsCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="yaml" showLineNumbers>{InspectEnvironmentsOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::important
|
||||
CUE populates the `config` field on the first line of output from the `config: _ @embed(...)`
|
||||
lines in `environments.cue`.
|
||||
:::
|
||||
|
||||
Holos and CUE offer multiple improvements over the ApplicationSet we're
|
||||
migrating from.
|
||||
|
||||
1. **Validation** - Each `config.json` file is validated against the `#Config` schema we defined.
|
||||
2. **Ergonomics** - Holos enables fast, local queries over one unified
|
||||
configuration. The data is at hand. We don't need a controller running in a
|
||||
remote cluster. We can query the intermediate config data structure, improving
|
||||
our ability to troubleshoot problems the same way an interactive debugger gives
|
||||
access to intermediate state.
|
||||
3. **Flexibility** - We're no longer locked into ArgoCD functionality. The
|
||||
`environments` package can be imported and re-used across the unified platform
|
||||
configuration layer. We're able to continue processing the rendered manifests
|
||||
with other tools beyond just ArgoCD.
|
||||
|
||||
We've successfully migrated the ApplicationSet generator field to Holos. Next,
|
||||
we'll iterate over this structure to render `my-chart` for each environment.
|
||||
|
||||
### Rendering my-chart
|
||||
|
||||
The next step is to render `my-chart` into complete configuration manifests.
|
||||
Holos implements the same behavior as the Application's `spec.sources.helm`
|
||||
field. Both tools use Helm to render the chart. Holos is different though, it
|
||||
offers a flexible way to transform and validate the output of Helm, then stops
|
||||
once the manifest is written to a local file. ArgoCD doesn't persist rendered
|
||||
manifests, instead it applies them directly to the cluster.
|
||||
|
||||
Where Holos stops is another major difference from ArgoCD and all other tools
|
||||
operating as a controller in the cluster. Holos is designed for GitOps and
|
||||
integration with the rest of the Kubernetes ecosystem. Holos stops after it
|
||||
writes manifests to local files. This clear cut responsibility leaves ample
|
||||
space for subsequent automated workflow operating on the configuration Holos
|
||||
produces.
|
||||
|
||||
For example, consider a progressive delivery pipeline at the right side of this
|
||||
diagram to incrementally roll out configuration changes. ApplicationSets with a
|
||||
Helm source prevents this kind of integration. Holos with an Application Git
|
||||
source enables this kind of integration.
|
||||
|
||||
import RenderingOverview from '@site/src/diagrams/rendering-overview.mdx';
|
||||
|
||||
<RenderingOverview />
|
||||
|
||||
ArgoCD pairs well with other ecosystem tools when it keeps to what it does best:
|
||||
drift detection and reconciliation following GitOps principles. ArgoCD locks
|
||||
out other tools when it renders manifests. The configuration is transient and
|
||||
locked away in the cluster.
|
||||
|
||||
:::important
|
||||
Holos renders `my-chart` to local files, one for each of the environment configs
|
||||
we migrated to the `environments` package.
|
||||
:::
|
||||
|
||||
### Platform Components
|
||||
|
||||
The primary entrypoint for Holos is the `platform/` directory. The `holos
|
||||
render platform` command processes a [Platform] specification exported by CUE
|
||||
from this directory.
|
||||
|
||||
Each Application produced by the ApplicationSet we're migrating maps to a
|
||||
[Component] listed in the `Platform.spec.components` field. Here's how the
|
||||
components are added to the Platform in CUE.
|
||||
|
||||
{/* 987df87 add platform components to replace ApplicationSets.spec.generators */}
|
||||
|
||||
import PlatformChartHeader from '!!raw-loader!./_migrate_appset/script-04-helm-component/platform-my-chart-header.sh';
|
||||
import PlatformChartBody from '!!raw-loader!./_migrate_appset/script-04-helm-component/platform-my-chart.cue';
|
||||
import PlatformChartTrailer from '!!raw-loader!./_migrate_appset/script-04-helm-component/platform-my-chart-trailer.sh';
|
||||
|
||||
<CodeBlock language="bash">{PlatformChartHeader}</CodeBlock>
|
||||
<CodeBlock language="cue" showLineNumbers>{PlatformChartBody}</CodeBlock>
|
||||
<CodeBlock language="bash">{PlatformChartTrailer}</CodeBlock>
|
||||
|
||||
For each of the data objects defined in the `config.json` files, we define a
|
||||
field in the `Platform.Components` struct. We use a struct for convenience,
|
||||
it's easier to compose components into a struct than it is into an ordered list.
|
||||
The Platform author schema converts this struct into the `spec.components` list.
|
||||
|
||||
`#MyChart` is a schema definition acting as a reusable template. For each of
|
||||
the environment config files we build the component configuration from
|
||||
parameters. This is an example of how we compose configuration. The
|
||||
`outputBaseDir` field is composed in from the `env` field configured in the
|
||||
original `config.json` files migrated to CUE.
|
||||
|
||||
We need to add a configuration snippet so each component accepts this parameter
|
||||
and renders manifests into folders organized by environment. The use of `@tag`
|
||||
with the `OutputBaseDir` field indicates the field value is provided by the
|
||||
Platform spec when we run `holos render platform`.
|
||||
|
||||
import ComponentConfigHeader from '!!raw-loader!./_migrate_appset/script-04-helm-component/componentconfig-header.sh';
|
||||
import ComponentConfigBody from '!!raw-loader!./_migrate_appset/script-04-helm-component/componentconfig.cue';
|
||||
import ComponentConfigTrailer from '!!raw-loader!./_migrate_appset/script-04-helm-component/componentconfig-trailer.sh';
|
||||
|
||||
<CodeBlock language="bash">{ComponentConfigHeader}</CodeBlock>
|
||||
<CodeBlock language="cue" showLineNumbers>{ComponentConfigBody}</CodeBlock>
|
||||
<CodeBlock language="bash">{ComponentConfigTrailer}</CodeBlock>
|
||||
|
||||
We can gain insight into how `holos` renders the helm charts from the
|
||||
`config.json` files with the following command. CUE exports the `Platform`
|
||||
specification to `holos`, which iterates over each of the listed components to
|
||||
produce a `BuildPlan`.
|
||||
|
||||
import ShowPlatformCommand from '!!raw-loader!./_migrate_appset/script-04-helm-component/holos-show-platform.sh';
|
||||
import ShowPlatformOutput from '!!raw-loader!./_migrate_appset/script-04-helm-component/holos-show-platform.txt';
|
||||
|
||||
<Tabs groupId="show-platform">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{ShowPlatformCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="yaml" showLineNumbers>{ShowPlatformOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Component Definition
|
||||
|
||||
The next step is to wrap `my-chart` in a Holos [Helm] component definition.
|
||||
Here's how:
|
||||
|
||||
import ComponentHeader from '!!raw-loader!./_migrate_appset/script-04-helm-component/component-my-chart-header.sh';
|
||||
import ComponentBody from '!!raw-loader!./_migrate_appset/script-04-helm-component/component-my-chart.cue';
|
||||
import ComponentFooter from '!!raw-loader!./_migrate_appset/script-04-helm-component/component-my-chart-trailer.sh';
|
||||
|
||||
<CodeBlock language="bash">{ComponentHeader}</CodeBlock>
|
||||
<CodeBlock language="cue" showLineNumbers>{ComponentBody}</CodeBlock>
|
||||
<CodeBlock language="bash">{ComponentFooter}</CodeBlock>
|
||||
|
||||
Note how each parameter we added in the Platform spec is reflected in the
|
||||
component definition with a `@tag`. These are CUE build tags, and the mechanism
|
||||
by which parameters are passed from `holos render platform` to each component.
|
||||
|
||||
Similar to the `config.json` files we migrated, we moved the Helm value files
|
||||
without modifying them. These files are loaded into one struct in CUE using
|
||||
`valueFiles: _ @embed(...)`.
|
||||
|
||||
Like the Platform spec, we can inspect the BuildPlans `holos` executes to render
|
||||
each component to manifest files.
|
||||
|
||||
import ShowBuildPlansCmd from '!!raw-loader!./_migrate_appset/script-04-helm-component/show-buildplans.sh'
|
||||
import ShowBuildPlansOut from '!!raw-loader!./_migrate_appset/script-04-helm-component/show-buildplans.txt'
|
||||
|
||||
<Tabs groupId="show-buildplans">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{ShowBuildPlansCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="yaml" showLineNumbers>{ShowBuildPlansOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
We can also inspect intermediate configuration like the `valueFiles` struct.
|
||||
|
||||
import ValueFilesCommand from '!!raw-loader!./_migrate_appset/script-04-helm-component/inspect-value-files.sh'
|
||||
import ValueFilesOutput from '!!raw-loader!./_migrate_appset/script-04-helm-component/inspect-value-files.txt'
|
||||
|
||||
<Tabs groupId="inspect-value-files">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{ValueFilesCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="yaml" showLineNumbers>{ValueFilesOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Render the platform to render `my-chart` for each of the configured
|
||||
environments.
|
||||
|
||||
import RenderCommand from '!!raw-loader!./_migrate_appset/script-04-helm-component/render.sh'
|
||||
import RenderOutput from '!!raw-loader!./_migrate_appset/script-04-helm-component/render.txt'
|
||||
|
||||
<CodeBlock language="bash">{RenderCommand}</CodeBlock>
|
||||
<CodeBlock language="txt">{RenderOutput}</CodeBlock>
|
||||
|
||||
Holos processes the Platform spec.components field concurrently, rendering each
|
||||
environment to a manifest file into the `deploy` folder. The output looks like:
|
||||
|
||||
import TreeCommand from '!!raw-loader!./_migrate_appset/script-04-helm-component/tree-deploy.sh'
|
||||
import TreeOutput from '!!raw-loader!./_migrate_appset/script-04-helm-component/tree-deploy.txt'
|
||||
|
||||
<Tabs groupId="tree-deploy">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{TreeCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{TreeOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### ArgoCD Application
|
||||
|
||||
At this point we're rendering `my-chart` with Holos for each environment. The
|
||||
intermediate and final configuration is within our reach. The final step is to
|
||||
render an Application resource for each environment like the original
|
||||
ApplicationSet did.
|
||||
|
||||
Holos offers [ComponentConfig] for the purpose of mixing in configuration to
|
||||
components. The feature is often used to pass each component through
|
||||
`kustomize` to add common labels and annotations. It's also used to mix in
|
||||
GitOps resources like ArgoCD Applications and Flux Kustomizations.
|
||||
|
||||
Here's how to add an Application for every one of the `Platform` components:
|
||||
|
||||
{/* d9125b8 compose argocd application resources into every component */}
|
||||
|
||||
import ComponentConfigGitOpsHeader from '!!raw-loader!./_migrate_appset/script-05-application/componentconfig-gitops-header.sh';
|
||||
import ComponentConfigGitOpsBody from '!!raw-loader!./_migrate_appset/script-05-application/componentconfig-gitops.cue';
|
||||
import ComponentConfigGitOpsTrailer from '!!raw-loader!./_migrate_appset/script-05-application/componentconfig-gitops-trailer.sh';
|
||||
|
||||
<CodeBlock language="bash">{ComponentConfigGitOpsHeader}</CodeBlock>
|
||||
<CodeBlock language="cue" showLineNumbers>{ComponentConfigGitOpsBody}</CodeBlock>
|
||||
<CodeBlock language="bash">{ComponentConfigGitOpsTrailer}</CodeBlock>
|
||||
|
||||
More information about how this works is available in the following pages. For
|
||||
now, it's sufficient to know the ComponentConfig is something we usually set and
|
||||
forget.
|
||||
|
||||
1. [ComponentConfig]
|
||||
2. [GitOps](/docs/v1alpha5/topics/gitops/)
|
||||
|
||||
Now we can render the platform and see each of the Application manifest files.
|
||||
They go into a `gitops` folder so it easy to apply them individually or all at
|
||||
once for ArgoCD to sync the component manifests.
|
||||
|
||||
import AppRenderCommand from '!!raw-loader!./_migrate_appset/script-05-application/render.sh'
|
||||
import AppRenderOutput from '!!raw-loader!./_migrate_appset/script-05-application/render.txt'
|
||||
|
||||
<CodeBlock language="bash">{AppRenderCommand}</CodeBlock>
|
||||
<CodeBlock language="txt">{AppRenderOutput}</CodeBlock>
|
||||
|
||||
<Tabs groupId="tree-deploy-with-application">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{AppTreeCommand}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{AppTreeOutput}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The Applications are also fully rendered.
|
||||
|
||||
<CodeBlock language="txt">{AppPath}</CodeBlock>
|
||||
<CodeBlock language="yaml">{AppYAML}</CodeBlock>
|
||||
|
||||
Note how the Application resources Holos produces are easier to read and
|
||||
understand than the original ApplicationSet.
|
||||
|
||||
1. There is no templating.
|
||||
2. There is no helm source, no value hierarchy to comprehend.
|
||||
|
||||
We also have the fully rendered manifest clearly readable and within reach locally.
|
||||
|
||||
<CodeBlock language="yaml">{"# "+ManifestPath+ManifestYAML}</CodeBlock>
|
||||
|
||||
## Wrapping it all up
|
||||
|
||||
1. The manifests are fully rendered and within our reach.
|
||||
2. The Application is clear and at-hand.
|
||||
3. We now have a unified platform configuration layer.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Part 2 - Why are there 8 config.json files but only 7 components rendered? There's a bug!
|
||||
- Part 3 - Can we eliminate the layers of helm value overrides? Yes!
|
||||
- Part 4 - Progressive Delivery. Maybe?
|
||||
|
||||
[tfa]: https://medium.com/containers-101/using-helm-hierarchies-in-multi-source-argo-cd-applications-for-promoting-to-different-gitops-133c3bc93678
|
||||
[ApplicationSet]: https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml
|
||||
[multi-sources-example]: https://github.com/holos-run/multi-sources-example
|
||||
[Platform]: https://holos.run/docs/api/core/#Platform
|
||||
[Component]: https://holos.run/docs/api/core/#Component
|
||||
[ComponentConfig]: https://holos.run/docs/api/author/#ComponentConfig
|
||||
[Helm]: https://holos.run/docs/api/core/#Helm
|
||||
[CUE]: https://cuelang.org
|
||||
[Install Holos]: https://holos.run/docs/setup/
|
||||
752
doc/website/blog/2025-01-17-unify-helm-value-hierarchy.mdx
Normal file
752
doc/website/blog/2025-01-17-unify-helm-value-hierarchy.mdx
Normal file
@@ -0,0 +1,752 @@
|
||||
---
|
||||
unlisted: true
|
||||
slug: simplify-helm-value-files-with-unification
|
||||
title: Simplify Helm Value Files with Unification
|
||||
authors: [jeff]
|
||||
tags: [holos, helm, gitops]
|
||||
# image: /img/cards/validators.png
|
||||
description: Migrate an ApplicationSet to the rendered manifest pattern with Holos, removing unnecessary complexity.
|
||||
keywords:
|
||||
- Holos
|
||||
- CUE
|
||||
- Configuration
|
||||
- Kubernetes
|
||||
- Hydrated
|
||||
- Rendered
|
||||
- Manifest
|
||||
- Pattern
|
||||
- Rendered Manifest Pattern
|
||||
- Unification
|
||||
- ArgoCD
|
||||
- ApplicationSet
|
||||
- Application
|
||||
- Multi Source
|
||||
- Values
|
||||
- Hierarchy
|
||||
- Merge
|
||||
- Override
|
||||
- GitOps
|
||||
- Complexity
|
||||
---
|
||||
|
||||
## Simplifying a Hierarchy
|
||||
|
||||
In [Part 1] of our series on migrating a complex multi environment
|
||||
ApplicationSet to Holos we reduced complexity by replacing the ApplicationSet Go
|
||||
template generator with an Application resource exported from CUE. We completed
|
||||
the migration to implement the rendered manifest pattern, but complexity remains
|
||||
in the 5 layers of Helm value file overrides.
|
||||
|
||||
We'll continue eliminating accidental complexity by refactoring the Helm value
|
||||
file hierarchy into one unified layer. The data will be visible and within
|
||||
reach through the command line. Any future changes will either unify
|
||||
successfully or produce an immediate error, significantly reducing the
|
||||
complexity of managing Kubernetes by eliminating the accidental complexity of a
|
||||
Helm value hierarchy.
|
||||
|
||||
[Part 1]: ./2025-01-13-replace-an-applicationset-with-the-rendered-manifest-pattern.mdx
|
||||
|
||||
{/* truncate */}
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
### Why are overrides a problem?
|
||||
|
||||
Consider this snippet form the [original ApplicationSet] we migrated:
|
||||
|
||||
```yaml
|
||||
helm:
|
||||
valueFiles:
|
||||
- $values/my-values/common-values.yaml
|
||||
- $values/my-values/app-version/{{.version}}-values.yaml
|
||||
- $values/my-values/env-type/{{.type}}-values.yaml
|
||||
- $values/my-values/regions/{{.region}}-values.yaml
|
||||
- $values/my-values/envs/{{.env}}-values.yaml
|
||||
```
|
||||
|
||||
Both Holos and ArgoCD pass these value files to Helm in order, resulting in the
|
||||
following Helm command:
|
||||
|
||||
```bash
|
||||
helm template my-chart \
|
||||
-f my-values/common-values.yaml \
|
||||
-f my-values/app-version/prod-values.yaml \
|
||||
-f my-values/env-type/prod-values.yaml \
|
||||
-f my-values/regions/us-values.yaml \
|
||||
-f my-values/envs/prod-us-values.yaml \
|
||||
...
|
||||
```
|
||||
|
||||
Helm merges each of the value files on top of one another in a last in first out
|
||||
manner. Imagine we're troubleshooting an issue where the Deployment in the live
|
||||
system has `replicas: 3` but it should have `replicas: 5`. We need to
|
||||
reconfigure the replicas.
|
||||
|
||||
1. **It's unclear where the `replicas: 3` value came from** when looking at the live system.
|
||||
2. **It's unclear if or where `replicaCount` is used** when looking at each value file.
|
||||
3. **It's unclear where we need to set a value of `5` to fix the problem.** Common
|
||||
values would probably affect too many deployments. Prod-us values would likely
|
||||
be too specific, missing other prod environments.
|
||||
4. **Complexity accumulates over time.** Each time we make a change we're invited
|
||||
to add a new level of overrides. If we're adding a value that's not broad
|
||||
enough at one level, but too broad at the next level, then we have no choice but
|
||||
to add one more level in between.
|
||||
5. **It's difficult to see the actual values passed to Helm.** We have to implement
|
||||
the last in first out merge algorithm in our head as we page through each file,
|
||||
an extremely error prone chore.
|
||||
|
||||
For example, `common-values.yaml` has `replicaCount: 1`. Is that value actually
|
||||
used anywhere? The only way to know is to walk through all permutations and
|
||||
determine if it's overridden or not. Spoiler, `common-values.yaml` is _always_
|
||||
overridden in the original ApplicationSet we migrated. It serves no purpose,
|
||||
yet it lays a hidden trap for us to trip over in the future. If we remove a
|
||||
value overriding common-values.yaml then we don't know if the freshly uncovered
|
||||
value had an important purpose relevant now or if it no longer serves a purpose.
|
||||
|
||||
This particular form of override hierarchy is similar to inheritance, suffering
|
||||
from the same problems. The central concept of unification in CUE elegantly
|
||||
solves these problems.
|
||||
|
||||
> Like with other configuration languages, CUE can add complexity if values are organized to come from multiple places. However, as CUE disallows overrides, deep layerings are naturally prevented. More importantly, CUE can also enhance readability. A definition in one file may apply to values in many other files. Where one would usually have to open all these files to verify validity; with CUE one can see it at a glance.
|
||||
|
||||
Attribution: [Simplicity at Scale](https://cuelang.org/docs/concept/configuration-use-case/#simplicity-at-scale)
|
||||
|
||||
> Inheritance is not commutative and idempotent in the general case. In other words, order matters. This makes it hard to track where values are coming from. This is not only true for humans, but also machines. It makes it very complicated, if not impossible, to do any kind of automation.
|
||||
>
|
||||
> The basic operation of CUE is commutative, associative and idempotent. This order independence helps both humans and machines. The resulting model is much less complex.
|
||||
|
||||
Attribution: [Inheritance-based configuration languages](https://cuelang.org/docs/concept/configuration-use-case/#inheritance-based-configuration-languages)
|
||||
|
||||
In CUE, **order is irrelevant**. This property greatly simplifies configuration
|
||||
as we'll see by unifying these value files.
|
||||
|
||||
### What's the desired outcome?
|
||||
|
||||
For this refactoring, we'd like to achieve the following goals:
|
||||
|
||||
1. Prune unused values.
|
||||
2. Reduce the five layers of abstraction down to one.
|
||||
3. Gain insight into the values passed into Helm.
|
||||
4. Fail fast if a future change creates a conflict.
|
||||
5. Fail fast if a necessary value is not provided.
|
||||
6. Prevent complex layers of abstraction from accumulating.
|
||||
7. Add type checks, schema validation, and constraints to Helm values.
|
||||
|
||||
### Starting Context
|
||||
|
||||
If you didn't work through [Part 1] the final results are available in the
|
||||
[end-of-part-1] branch for your reference.
|
||||
|
||||
### Pruning Unused Values
|
||||
|
||||
We need a way to identify which values are used so we can prune the unused ones.
|
||||
Holos simplifies this task by rendering all of the manifests to the local
|
||||
filesystem. We can simply grep the deploy directory to see what values are
|
||||
actually used.
|
||||
|
||||
import GrepReplicasCmd from '!!raw-loader!./_migrate_appset/script-06-unification/grep.sh';
|
||||
import GrepReplicasOut from '!!raw-loader!./_migrate_appset/script-06-unification/grep.txt';
|
||||
|
||||
<Tabs groupId="grep-replicas">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{GrepReplicasCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{GrepReplicasOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The `grep` output indicates **3, 8 and 10** are the only values used, yet
|
||||
`common-values.yaml` configures a value of 1.
|
||||
|
||||
import CommonPath from '!!raw-loader!./_migrate_appset/script-06-unification/common.path';
|
||||
import CommonYAML from '!!raw-loader!./_migrate_appset/script-06-unification/common.yaml';
|
||||
|
||||
<CodeBlock language="txt">{CommonPath}</CodeBlock>
|
||||
<CodeBlock language="yaml" showLineNumbers>{CommonYAML}</CodeBlock>
|
||||
|
||||
The `common-values.yaml` file is therefore useless. Let's remove it.
|
||||
|
||||
import RemoveHead from '!!raw-loader!./_migrate_appset/script-06-unification/remove.head';
|
||||
import RemoveBody from '!!raw-loader!./_migrate_appset/script-06-unification/remove.body';
|
||||
import RemoveTail from '!!raw-loader!./_migrate_appset/script-06-unification/remove.tail';
|
||||
import RemoveOut from '!!raw-loader!./_migrate_appset/script-06-unification/remove.out';
|
||||
|
||||
<Tabs groupId="remove-common-values">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{RemoveHead}</CodeBlock>
|
||||
<CodeBlock language="diff" showLineNumbers>{RemoveBody}</CodeBlock>
|
||||
<CodeBlock language="bash">{RemoveTail}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{RemoveOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
We double check by rendering the platform. There should be no changes to the
|
||||
deploy directory even though we removed `common-values.yaml` from the Helm
|
||||
hierarchy.
|
||||
|
||||
import RenderCmd from '!!raw-loader!./_migrate_appset/script-06-unification/render.sh';
|
||||
import RenderOut from '!!raw-loader!./_migrate_appset/script-06-unification/render.txt';
|
||||
|
||||
<Tabs groupId="render-command">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{RenderCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{RenderOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
import CheckCmd from '!!raw-loader!./_migrate_appset/script-06-unification/check.sh';
|
||||
import CheckOut from '!!raw-loader!./_migrate_appset/script-06-unification/check.txt';
|
||||
|
||||
<Tabs groupId="git-status">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{CheckCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{CheckOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::tip
|
||||
Holos makes it easier to confidently remove values which are never used.
|
||||
:::
|
||||
|
||||
If we didn't have the rendered manifests in hand this would be a much more
|
||||
difficult task.
|
||||
|
||||
### Eliminating Complexity
|
||||
|
||||
Let's unify all the levels of the value hierarchy into one layer and see where
|
||||
the conflicts are. This is straight forward with `holos`.
|
||||
|
||||
:::important
|
||||
One unified layer is significantly less complicated than a hierarchy of
|
||||
overrides.
|
||||
:::
|
||||
|
||||
import UnifyHead from '!!raw-loader!./_migrate_appset/script-06-unification/unify.head';
|
||||
import UnifyBody from '!!raw-loader!./_migrate_appset/script-06-unification/unify.body';
|
||||
import UnifyTail from '!!raw-loader!./_migrate_appset/script-06-unification/unify.tail';
|
||||
import UnifyOut from '!!raw-loader!./_migrate_appset/script-06-unification/unify.out';
|
||||
|
||||
<Tabs groupId="unify-values">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{UnifyHead}</CodeBlock>
|
||||
<CodeBlock language="diff" showLineNumbers>{UnifyBody}</CodeBlock>
|
||||
<CodeBlock language="bash">{UnifyTail}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{UnifyOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::important
|
||||
The 4 ordered list elements convert into one unified struct.
|
||||
:::
|
||||
|
||||
Render all components to see conflicting values. We use concurrency 1 to return
|
||||
the first error encountered.
|
||||
|
||||
import Render2Cmd from '!!raw-loader!./_migrate_appset/script-06-unification/render2.sh';
|
||||
import Render2Out from '!!raw-loader!./_migrate_appset/script-06-unification/render2.txt';
|
||||
|
||||
<Tabs groupId="render2-command">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{Render2Cmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{Render2Out}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The `prod-eu` environment has a conflict:
|
||||
|
||||
```txt
|
||||
values.replicaCount: conflicting values 8 and 5
|
||||
```
|
||||
|
||||
The error is detailed, helpfully listing all of the potential locations the
|
||||
conflict may arise from. We can hone in on the yaml files in the `my-values`
|
||||
directory:
|
||||
|
||||
1. my-values/env-type/prod-values.yaml:2:15
|
||||
2. my-values/envs/prod-eu-values.yaml:2:15
|
||||
|
||||
These files we originally migrated without modifying them are:
|
||||
|
||||
import ConflictA from '!!raw-loader!./_migrate_appset/script-06-unification/conflict/prod-values.yaml';
|
||||
import ConflictB from '!!raw-loader!./_migrate_appset/script-06-unification/conflict/prod-eu-values.yaml';
|
||||
|
||||
<Tabs groupId="conflict-files">
|
||||
<TabItem value="prod-values.yaml" label="prod-values.yaml">
|
||||
<CodeBlock language="yaml" showLineNumbers>{ConflictA}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="prod-eu-values.yaml" label="prod-eu-values.yaml">
|
||||
<CodeBlock language="yaml" showLineNumbers>{ConflictB}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::important
|
||||
The conflict arises from the original article specifying a value for the same
|
||||
field along two different aspects of our configuration: specific environments
|
||||
(envs) and broad types of environments (env-types).
|
||||
:::
|
||||
|
||||
### Resolving Conflicts
|
||||
|
||||
In Holos and CUE we resolve these conflicts by picking one aspect to specify a
|
||||
given field value. We'll pick specific environments in this case because it's
|
||||
not too broad and not too specific.
|
||||
|
||||
We previously determined **3, 8 and 10** are the only values actually used in
|
||||
the final configurations, so we already know `replicaCount: 5` in the
|
||||
`my-values/env-type/prod-values.yaml` file is useless.
|
||||
|
||||
Let's remove it.
|
||||
|
||||
import Unify2Head from '!!raw-loader!./_migrate_appset/script-06-unification/unify2.head';
|
||||
import Unify2Body from '!!raw-loader!./_migrate_appset/script-06-unification/unify2.body';
|
||||
import Unify2Tail from '!!raw-loader!./_migrate_appset/script-06-unification/unify2.tail';
|
||||
import Unify2Out from '!!raw-loader!./_migrate_appset/script-06-unification/unify2.out';
|
||||
|
||||
<Tabs groupId="unify2-values">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{Unify2Head}</CodeBlock>
|
||||
<CodeBlock language="diff" showLineNumbers>{Unify2Body}</CodeBlock>
|
||||
<CodeBlock language="bash">{Unify2Tail}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{Unify2Out}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Now we can render the platform again and verify there are no changes to the
|
||||
rendered manifests in the `deploy` directory.
|
||||
|
||||
import Render3Cmd from '!!raw-loader!./_migrate_appset/script-06-unification/render3.sh';
|
||||
import Render3Out from '!!raw-loader!./_migrate_appset/script-06-unification/render3.txt';
|
||||
|
||||
<Tabs groupId="render3-command">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{Render3Cmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{Render3Out}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
import Status3Cmd from '!!raw-loader!./_migrate_appset/script-06-unification/status3.sh';
|
||||
import Status3Out from '!!raw-loader!./_migrate_appset/script-06-unification/status3.txt';
|
||||
|
||||
<Tabs groupId="status3-command">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{Status3Cmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{Status3Out}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Voila! We've successfully refactored all of the value files for Helm into a
|
||||
single unified data structure. We know we're done because unification succeeds
|
||||
and there are no changes to the deploy directory.
|
||||
|
||||
## Simplifying a Complex Hierarchy
|
||||
|
||||
We've removed the complexity of the Go template in the ApplicationSet, and 5
|
||||
more layers of complexity in the Helm value files, but the original data didn't
|
||||
have many overrides. It was the best case scenario, flattening the hierarchy by
|
||||
trial and error was straight forward.
|
||||
|
||||
What about a complex hierarchy with lots of overrides? This same trial and
|
||||
error approach would take too long. Holos offers a solution in this scenario.
|
||||
In this section we'll use `holos` to create a flattened value file for each of
|
||||
the config.json deployment configs without using trial and error.
|
||||
|
||||
The layout of the deployment configs is a key aspect of this method. The
|
||||
deployment config inputs always map 1:1 to the rendered manifests and associated
|
||||
Application resource. Therefore, any intermediate layers of overrides are not
|
||||
strictly necessary, an opportunity to reduce complexity significantly.
|
||||
|
||||
In this example the deployment configs are organized by customer and cluster, so
|
||||
we'll work to flatten the unnecessary levels of the hierarchy while also
|
||||
organizing the output around customers and clusters to get everything lined up
|
||||
nicely.
|
||||
|
||||
Using the same deployment configs at each step, we'll:
|
||||
|
||||
1. Render my-app using the helm values hierarchy to get a reference point.
|
||||
2. Render a special chart, render-values, to flatten the value files hierarchy into one file.
|
||||
3. Render my-app using the flattened values file.
|
||||
4. Verify there are no changes to the deploy artifacts.
|
||||
|
||||
We'll be able to quickly locate and read the configuration given a customer and
|
||||
a cluster.
|
||||
|
||||
### What's the worst case scenario?
|
||||
|
||||
In the worst case scenario there is little rhyme or reason to how values are set
|
||||
and overridden in a hierarchy. We'll start with this worst case scenario,
|
||||
spraying data across clusters and override layers. This starting point looks
|
||||
like the following, with the highest precedence values at the top of the table.
|
||||
|
||||
| Num | Layer | Description |
|
||||
| - | - | - |
|
||||
| 1 | Customer | customer-zzsbbmfc, customer-xxxxxxx, ... |
|
||||
| 2 | Namespace | prod-myapp, dev-myapp, ... |
|
||||
| 3 | Application | myapp, yourapp, ... |
|
||||
| 4 | Cluster | prod1-customer, dev2-internal, uat3-management, ... |
|
||||
| 5 | Environment | prod, uat, test, dev |
|
||||
| 6 | Tier | prod, nonprod |
|
||||
| 7 | Scope | customer, internal, management |
|
||||
| 8 | Zone | us-east1-a, us-east1-b, ... |
|
||||
| 9 | Region | us-east1, us-west1, ... |
|
||||
| 10 | Location | us, eu, ap, ... |
|
||||
| 11 | Common | Base layer |
|
||||
|
||||
Across these layers the following fields may be set:
|
||||
|
||||
| Field | Type | Description |
|
||||
| - | - | - |
|
||||
| enabled | bool | Feature flag |
|
||||
| image | string | Container image URI, e.g. "oci://example.com/myservice" |
|
||||
| version | string | Version string, e.g. "v0.1.0" |
|
||||
| domain | string | DNS domain, e.g. "example.com" |
|
||||
| replicas | int | number of replicas |
|
||||
| clientID | string | OAuth 2.0 client ID |
|
||||
| issuer | string | OIDC issuer uri, e.g. `https://login.example.com` |
|
||||
| projectID | string | cloud project id, e.g. "my-project-123456" |
|
||||
| accountID | int | cloud account id, e.g. 012345678901 |
|
||||
| arn | string | resource name, e.g. "arn:partition:service:region:account-id:resource-type:resource-id" |
|
||||
| cores | float | cpu cores, e.g. 2.0 |
|
||||
| memory | int | memory in MiB, e.g. 2048 |
|
||||
| labels | map[string]string | resource labels |
|
||||
|
||||
### Generating complexity
|
||||
|
||||
Generate the deployment configs and the value files. The RNG is seeded with
|
||||
constant values so the output is deterministic. If you'd like truly random data
|
||||
set `RANDOMIZE=1` in the environment before running this command..
|
||||
|
||||
import GeneratorCmd from '!!raw-loader!./_migrate_appset/script-08-complex-unification/generator.sh';
|
||||
|
||||
<CodeBlock language="bash">{GeneratorCmd}</CodeBlock>
|
||||
|
||||
{/* Show the generated structure */}
|
||||
|
||||
Deployment configs are generated in the following directory. The `config`
|
||||
directory contains concrete values by convention, organized in packages.
|
||||
|
||||
import ShowDeploymentConfigsCmd from '!!raw-loader!./_migrate_appset/script-08-complex-unification/show-deployment-configs.sh';
|
||||
import ShowDeploymentConfigsOut from '!!raw-loader!./_migrate_appset/script-08-complex-unification/show-deployment-configs.txt';
|
||||
|
||||
<CodeBlock language="bash">{ShowDeploymentConfigsCmd}</CodeBlock>
|
||||
<CodeBlock language="txt">{ShowDeploymentConfigsOut}</CodeBlock>
|
||||
|
||||
Each of these {ShowDeploymentConfigsOut} deployment config files map 1:1 to a
|
||||
`holos` `BuildPlan` and an ArgoCD `Application`.
|
||||
|
||||
The Helm value files hierarchy files are written into the `my-app` component
|
||||
directory. By convention values reside in the component directory so they're
|
||||
closely associated with the helm chart using them.
|
||||
|
||||
import ShowValueFilesCmd from '!!raw-loader!./_migrate_appset/script-08-complex-unification/show-value-files.sh';
|
||||
import ShowValueFilesOut from '!!raw-loader!./_migrate_appset/script-08-complex-unification/show-value-files.txt';
|
||||
|
||||
<Tabs groupId="show-value-files">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{ShowValueFilesCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{ShowValueFilesOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Flattening Step 1
|
||||
|
||||
We'll use CUE build tags to render `my-app` instead of `my-chart` for this
|
||||
example. Build tags alow us to exclude files used in the previous example, like
|
||||
`platform/my-chart.cue`, and include files for this example like
|
||||
`platform/my-app.cue`.
|
||||
|
||||
This example is about flattening the helm values file, so we use `-t flatten`.
|
||||
There are three steps to flatten the hierarchy, so we'll use `-t stepX` for each
|
||||
step along the way.
|
||||
|
||||
Finally, we'll use selectors to focus on one customer specifically. It takes a
|
||||
few minutes (about 4 on my machine) to render all {ShowDeploymentConfigsOut}
|
||||
deployments for all customers, so we can develop and test the migration process
|
||||
quickly with one customer, then roll the change out to all customers once we're
|
||||
ready.
|
||||
|
||||
Let's take a look at one `BuildPlan` for one customer. Take note of the
|
||||
valueFiles hierarchy in the output. Our goal is to reduce complexity by
|
||||
collapsing this down to one layer with at most one override of a default value.
|
||||
|
||||
First, set the customer as a variable:
|
||||
|
||||
```bash
|
||||
export CUSTOMER="customer-zzsbbmfc"
|
||||
```
|
||||
|
||||
import ShowOneBuildPlanCmd from '!!raw-loader!./_migrate_appset/script-08-complex-unification/show-one-buildplan.sh';
|
||||
import ShowOneBuildPlanOut from '!!raw-loader!./_migrate_appset/script-08-complex-unification/show-one-buildplan.txt';
|
||||
|
||||
<Tabs groupId="show-one-buildplan">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{ShowOneBuildPlanCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="yaml" showLineNumbers>{ShowOneBuildPlanOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The other `BuildPlan` specifications are similar. Values are randomly
|
||||
distributed through the hierarchy making it a challenge to comprehend which
|
||||
value is used in the final configuration.
|
||||
|
||||
Render the final manifests for this customer so we have a reference point for
|
||||
the rest of the migration.
|
||||
|
||||
import RenderOneCustomerCmd from '!!raw-loader!./_migrate_appset/script-08-complex-unification/render-one-customer.sh';
|
||||
import RenderOneCustomerOut from '!!raw-loader!./_migrate_appset/script-08-complex-unification/render-one-customer.txt';
|
||||
|
||||
<Tabs groupId="render-one-customer">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{RenderOneCustomerCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{RenderOneCustomerOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Add and commit these changes so we can easily see any future changes. For the
|
||||
rest of the migration there should be no changes to the final configuration.
|
||||
|
||||
import AddCommitStep1Cmd from '!!raw-loader!./_migrate_appset/script-08-complex-unification/add-and-commit-step1.sh';
|
||||
import AddCommitStep1Out from '!!raw-loader!./_migrate_appset/script-08-complex-unification/add-and-commit-step1.txt';
|
||||
|
||||
<Tabs groupId="add-commit-step1">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{AddCommitStep1Cmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{AddCommitStep1Out}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Flattening Step 2
|
||||
|
||||
Now that we have a reference point we can use `holos` to flatten the hierarchy.
|
||||
We'll pass the same value files to a special `render-values` chart that simply
|
||||
echos back the final merged values. Holos writes the value files to
|
||||
`deploy/values` organized by customer and cluster to match the organization of
|
||||
the source deployment configs, the `config.json` files.
|
||||
|
||||
import RenderStep2Cmd from '!!raw-loader!./_migrate_appset/script-08-complex-unification/render-step2.sh';
|
||||
import RenderStep2Out from '!!raw-loader!./_migrate_appset/script-08-complex-unification/render-step2.txt';
|
||||
|
||||
<Tabs groupId="render-step2">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{RenderStep2Cmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{RenderStep2Out}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
This command produces flattened `values.yaml` files for each one of the input
|
||||
deployment configs.
|
||||
|
||||
import ShowFlattenedValuesCmd from '!!raw-loader!./_migrate_appset/script-08-complex-unification/show-flattened-values.sh';
|
||||
import ShowFlattenedValuesOut from '!!raw-loader!./_migrate_appset/script-08-complex-unification/show-flattened-values.txt';
|
||||
|
||||
<Tabs groupId="show-flattened-values">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{ShowFlattenedValuesCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{ShowFlattenedValuesOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Now we can proceed to change the `my-app` Holos component to use these flattened
|
||||
values in place of the value files hierarchy.
|
||||
|
||||
### Flattening Step 3
|
||||
|
||||
The final step of the migration is to use the flattened values in place of the
|
||||
complex hierarchy. In the component definition we replace the use of
|
||||
`valueFiles` with one `values` struct.
|
||||
|
||||
Normally we'd move the `deploy/values` directory into `components/my-app`, but
|
||||
for the article we use a symlink instead: `components/my-app/flattened-values ->
|
||||
../../deploy/values`
|
||||
|
||||
Here's how the final `BuildPlan` looks. Note how the values are flattened and
|
||||
the complexity of the override layers are gone.
|
||||
|
||||
import Step3ShowOneBuildPlanCmd from '!!raw-loader!./_migrate_appset/script-09-step3/show-one-buildplan.sh';
|
||||
import Step3ShowOneBuildPlanOut from '!!raw-loader!./_migrate_appset/script-09-step3/show-one-buildplan.txt';
|
||||
|
||||
<Tabs groupId="step3-show-one-buildplan">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{Step3ShowOneBuildPlanCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="yaml" showLineNumbers>{Step3ShowOneBuildPlanOut}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Step 1" label="Step 1 Comparison">
|
||||
This is the previous BuildPlan from Step 1 for comparison.
|
||||
<CodeBlock language="yaml" showLineNumbers>{ShowOneBuildPlanOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Render the platform to ensure we're producing the same output as we did when we
|
||||
started in step 1.
|
||||
|
||||
import RenderStep3Cmd from '!!raw-loader!./_migrate_appset/script-09-step3/render-one-customer.sh';
|
||||
import RenderStep3Out from '!!raw-loader!./_migrate_appset/script-09-step3/render-one-customer.txt';
|
||||
|
||||
<Tabs groupId="render-step3">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{RenderStep3Cmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{RenderStep3Out}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The manifests rendered with the flattened values are identical to those rendered
|
||||
with the more complex hierarchy of overrides.
|
||||
|
||||
import GitStatusStep3Cmd from '!!raw-loader!./_migrate_appset/script-09-step3/git-status.sh';
|
||||
import GitStatusStep3Out from '!!raw-loader!./_migrate_appset/script-09-step3/git-status.txt';
|
||||
|
||||
<Tabs groupId="git-status-step3">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{GitStatusStep3Cmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="txt">{GitStatusStep3Out}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Going deeper
|
||||
|
||||
If you'd like to dive deeper into these steps, take a look at the following
|
||||
files which are the entrypoints for the `holos render platform` command at each
|
||||
step. From there, take a look at the component definitions in
|
||||
`components/my-app/*.cue` and `components/render-values/*.cue`
|
||||
|
||||
#### Entrypoints
|
||||
|
||||
import S2PlatformMyApp from '!!raw-loader!./_migrate_appset/script-08-complex-unification/files/platform-my-app.cue';
|
||||
import S2PlatformRenderValues from '!!raw-loader!./_migrate_appset/script-08-complex-unification/files/platform-render-values.cue';
|
||||
|
||||
<Tabs groupId="review">
|
||||
<TabItem value="Step 1" label="Step 1">
|
||||
```
|
||||
platform/my-app.cue
|
||||
```
|
||||
<CodeBlock language="cue" showLineNumbers>{S2PlatformMyApp}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Step 2" label="Step 2">
|
||||
```
|
||||
platform/render-values.cue
|
||||
```
|
||||
<CodeBlock language="cue" showLineNumbers>{S2PlatformRenderValues}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Step 3" label="Step 3">
|
||||
:::note
|
||||
Step 3 uses the same entrypoint as Step 1, completing the migration. The
|
||||
main difference is in `components/my-app/step3.cue` where the flattened
|
||||
values are used.
|
||||
:::
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### Component Definition
|
||||
|
||||
The `my-app` component definition starts off in step 1, is not used in step 2,
|
||||
and changes to a flattened values structure in step3. Here are the changes at
|
||||
each step.
|
||||
|
||||
import ComponentDefMyAppXMain from '!!raw-loader!./_migrate_appset/script-09-step3/files/my-app.cue';
|
||||
import ComponentDefMyAppStep1 from '!!raw-loader!./_migrate_appset/script-09-step3/files/step1.cue';
|
||||
import ComponentDefMyAppStep3 from '!!raw-loader!./_migrate_appset/script-09-step3/files/step3.cue';
|
||||
|
||||
<Tabs groupId="review-component-definition">
|
||||
<TabItem value="Component Definition" label="Component Definition">
|
||||
```
|
||||
components/my-app/my-app.cue
|
||||
```
|
||||
<CodeBlock language="cue" showLineNumbers>{ComponentDefMyAppXMain}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Step 1" label="Step 1">
|
||||
```
|
||||
components/my-app/step1.cue
|
||||
```
|
||||
<CodeBlock language="cue" showLineNumbers>{ComponentDefMyAppStep1}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Step 3" label="Step 3">
|
||||
```
|
||||
components/my-app/step3.cue
|
||||
```
|
||||
<CodeBlock language="cue" showLineNumbers>{ComponentDefMyAppStep3}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Concluding Remarks
|
||||
|
||||
The original article used 5 layers of Helm value files. Migrating the
|
||||
configuration to Holos uncovered issues and solved problems.
|
||||
|
||||
1. `replicaCount: 1` in common-values.yaml was unnecessary, it was always overridden.
|
||||
2. `replicaCount: 5` in prod-values.yaml was _also_ unnecessary, it was never used.
|
||||
3. Removing these two values allowed unification to succeed, which indicates
|
||||
there was no need for a Helm value hierarchy at all.
|
||||
|
||||
The Helm value hierarchy added significant accidental complexity, accidental
|
||||
because it was not necessary and complex because the layers of override made it
|
||||
difficult to reason about the system.
|
||||
|
||||
Migrating to Holos achieves the same result, the deploy directory remains
|
||||
unchanged, while leaving us with a much simpler solution.
|
||||
|
||||
1. **Complexity remains constant over time.** New aspects can be added, for
|
||||
example specifying values by customer without introducing a new layer of
|
||||
overrides. With the previous approach of Helm value overrides _each additional
|
||||
layer accidentally increased complexity_.
|
||||
2. **Conflicts are clear and immediate** indicating the exact files and line
|
||||
numbers where we need to resolve the conflict.
|
||||
3. **Data is within our reach** we no longer need to rely on ArgoCD running in a
|
||||
remote cluster to see the desired state. We can inspect the intermediate state
|
||||
with `holos show buildplans` and the final manifests with `holos render
|
||||
platform`. We can use simple tools like `grep` to make well informed decisions
|
||||
about the platform wide configuration.
|
||||
|
||||
For example, here's the `holos` `BuildPlan` for prod-us where we see the actual
|
||||
unified values provided to `helm`.
|
||||
|
||||
import ShowBuildPlansCmd from '!!raw-loader!./_migrate_appset/script-06-unification/buildplan.sh';
|
||||
import ShowBuildPlansOut from '!!raw-loader!./_migrate_appset/script-06-unification/buildplan.txt';
|
||||
|
||||
<Tabs groupId="show-buildplans-command">
|
||||
<TabItem value="Command" label="Command">
|
||||
<CodeBlock language="bash">{ShowBuildPlansCmd}</CodeBlock>
|
||||
</TabItem>
|
||||
<TabItem value="Output" label="Output">
|
||||
<CodeBlock language="yaml" showLineNumbers>{ShowBuildPlansOut}</CodeBlock>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
[Part 1]: ./2025-01-13-replace-an-applicationset-with-the-rendered-manifest-pattern.mdx
|
||||
[original ApplicationSet]: https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L27-L32
|
||||
[end-of-part-1]: https://github.com/holos-run/multi-sources-example/tree/end-of-part-1
|
||||
37
doc/website/blog/_migrate_appset/examples/01-clone.txtar
Normal file
37
doc/website/blog/_migrate_appset/examples/01-clone.txtar
Normal file
@@ -0,0 +1,37 @@
|
||||
# This doc tested with holos version...
|
||||
exec bash -c 'bash -euo pipefail $WORK/version.sh 2>&1'
|
||||
cmp stdout version.txt
|
||||
|
||||
# Remove the directory if it already exists
|
||||
exec rm -rf multi-sources-example
|
||||
# Clone the repository
|
||||
exec bash -c 'bash -euo pipefail $WORK/clone.sh 2>&1'
|
||||
cmp stdout clone.txt
|
||||
|
||||
# Get the git commit
|
||||
cd multi-sources-example
|
||||
exec git rev-parse --verify origin/HEAD
|
||||
cp stdout $WORK/git.commit
|
||||
|
||||
# Reset to TFA 4-final recommendation
|
||||
exec bash -c 'bash -euo pipefail $WORK/reset.sh 2>&1'
|
||||
cmp stdout $WORK/reset.txt
|
||||
|
||||
# Set the committer
|
||||
exec git config user.email go-test@example.com
|
||||
exec git config user.name 'go test'
|
||||
|
||||
-- version.sh --
|
||||
holos --version
|
||||
-- version.txt --
|
||||
0.103.0
|
||||
-- clone.sh --
|
||||
git clone https://github.com/holos-run/multi-sources-example.git
|
||||
cd multi-sources-example
|
||||
-- clone.txt --
|
||||
Cloning into 'multi-sources-example'...
|
||||
-- reset.sh --
|
||||
git branch -f work start
|
||||
git checkout work
|
||||
-- reset.txt --
|
||||
Switched to branch 'work'
|
||||
@@ -0,0 +1,25 @@
|
||||
# Work in the root of the example repo
|
||||
cd ../script-01-clone/multi-sources-example
|
||||
|
||||
# Reset to where 01-clone left us.
|
||||
exec git clean -fdx
|
||||
exec git reset --hard start
|
||||
|
||||
# Consider the ApplicationSet from the final recommendation...
|
||||
exec bash -c 'cat $(<$WORK/appset.path)'
|
||||
cp stdout $WORK/appset.yaml
|
||||
|
||||
# The Deployment
|
||||
exec bash -c 'cat $(<$WORK/deployment.path)'
|
||||
cp stdout $WORK/deployment.yaml
|
||||
|
||||
# The Service
|
||||
exec bash -c 'cat $(<$WORK/service.path)'
|
||||
cp stdout $WORK/service.yaml
|
||||
|
||||
-- appset.path --
|
||||
appsets/4-final/all-my-envs-appset-with-version.yaml
|
||||
-- service.path --
|
||||
my-chart/templates/service.yaml
|
||||
-- deployment.path --
|
||||
my-chart/templates/deployment.yaml
|
||||
@@ -0,0 +1,83 @@
|
||||
# Work in the root of the example repo
|
||||
cd ../script-01-clone/multi-sources-example
|
||||
|
||||
# 7ce4feb initialize the platform
|
||||
exec bash -c 'bash -euo pipefail $WORK/holos-init.sh 2>&1'
|
||||
|
||||
# f7102d6 reorganize to conventional holos layout
|
||||
exec bash -c 'bash -euo pipefail $WORK/move-files-around.sh 2>&1'
|
||||
|
||||
# 49183ca git mv appsets/4-final/env-config config/environments
|
||||
# Note, the v0.3.0 tag contains the environments.cue prior to being updated to
|
||||
# fix the duplicated config.json file. Commit 1a73e77b fixes this issue as a
|
||||
# later migration step.
|
||||
exec cat $WORK/environments-header.sh $WORK/environments.cue $WORK/environments-trailer.sh
|
||||
stdin stdout
|
||||
exec bash -xeuo pipefail
|
||||
|
||||
# Inspect the structure
|
||||
exec bash -c 'bash -euo pipefail $WORK/inspect-environments.sh 2>&1'
|
||||
cp stdout $WORK/inspect-environments.txt
|
||||
|
||||
-- holos-init.sh --
|
||||
holos init platform v1alpha5 --force
|
||||
-- move-files-around.sh --
|
||||
# First, we'll move my-chart the original article vendored in
|
||||
# Git to the conventional location Holos uses to vendor charts.
|
||||
mkdir -p components/my-chart/vendor/0.1.0
|
||||
git mv my-chart components/my-chart/vendor/0.1.0/my-chart
|
||||
|
||||
# Helm value files move into the directory that will contain
|
||||
# my-chart component definition. components/my-chart is
|
||||
# conventionally called the "my-chart component directory"
|
||||
git mv my-values components/my-chart/my-values
|
||||
|
||||
# The config.json files are moved without changing their folder structure.
|
||||
# We'll package the data up into an "environments config package" for reuse.
|
||||
mkdir config
|
||||
git mv appsets/4-final/env-config config/environments
|
||||
|
||||
# All of our components will reside in the components directory so
|
||||
# the CUE files `holos init` produced may be moved to keep the
|
||||
# repository root tidy.
|
||||
mv *.cue components/
|
||||
|
||||
# The following files and directories from the original article and
|
||||
# holos init are no longer relevant after the migration.
|
||||
mkdir not-used
|
||||
git mv appsets not-used/appsets
|
||||
git mv example-apps not-used/example-apps
|
||||
rm -f platform.metadata.json
|
||||
|
||||
# Make the commit
|
||||
git add platform components cue.mod .gitignore
|
||||
git commit -m 'reorganize to conventional holos layout'
|
||||
-- environments-header.sh --
|
||||
cat <<'EOF' > config/environments/environments.cue
|
||||
-- environments.cue --
|
||||
@extern(embed)
|
||||
package environments
|
||||
|
||||
// We use cue embed functionality as an equivalent replacement for
|
||||
// ApplicationSet generators.
|
||||
config: _ @embed(glob=*/config.json)
|
||||
config: _ @embed(glob=staging/*/config.json)
|
||||
config: _ @embed(glob=prod/*/config.json)
|
||||
config: _ @embed(glob=integration/*/config.json)
|
||||
|
||||
// With CUE we can constrain the data with a schema.
|
||||
config: [FILEPATH=string]: #Config
|
||||
|
||||
// #Config defines the schema of each config.json file.
|
||||
#Config: {
|
||||
env: "qa" | "integration-gpu" | "integration-non-gpu" | "staging-us" | "staging-eu" | "prod-us" | "prod-eu"
|
||||
region: "us" | "eu"
|
||||
type: "prod" | "non-prod"
|
||||
version: "qa" | "staging" | "prod"
|
||||
chart: =~"^[0-9]+\\.[0-9]+\\.[0-9]+$"
|
||||
}
|
||||
-- environments-trailer.sh --
|
||||
EOF
|
||||
-- inspect-environments.sh --
|
||||
CUE_EXPERIMENT=embed holos cue export --out=yaml \
|
||||
./config/environments
|
||||
@@ -0,0 +1,205 @@
|
||||
# Work in the root of the example repo
|
||||
cd ../script-01-clone/multi-sources-example
|
||||
env HOME=$WORK/.tmp
|
||||
chmod 0755 $WORK/update.sh
|
||||
|
||||
# 987df87 add platform components to replace ApplicationSets.spec.generators
|
||||
exec cat $WORK/platform-my-chart-header.sh $WORK/platform-my-chart.cue $WORK/platform-my-chart-trailer.sh
|
||||
stdin stdout
|
||||
exec bash -xeuo pipefail
|
||||
exec diff platform/my-chart.cue $WORK/platform-my-chart.cue
|
||||
|
||||
# Configure where manifests are written.
|
||||
exec cat $WORK/componentconfig-header.sh $WORK/componentconfig.cue $WORK/componentconfig-trailer.sh
|
||||
stdin stdout
|
||||
exec bash -xeuo pipefail
|
||||
exec diff components/componentconfig.cue $WORK/componentconfig.cue
|
||||
|
||||
# Show the platform
|
||||
exec bash -c 'bash -euo pipefail $WORK/holos-show-platform.sh 2>&1'
|
||||
cp stdout $WORK/holos-show-platform.txt
|
||||
|
||||
# Component Definition
|
||||
exec cat $WORK/component-my-chart-header.sh $WORK/component-my-chart.cue $WORK/component-my-chart-trailer.sh
|
||||
stdin stdout
|
||||
exec bash -xeuo pipefail
|
||||
exec diff components/my-chart/my-chart.cue $WORK/component-my-chart.cue
|
||||
|
||||
# Show the BuildPlans
|
||||
exec bash -c 'bash -euo pipefail $WORK/show-buildplans.sh 2>&1'
|
||||
cp stdout $WORK/show-buildplans.txt
|
||||
|
||||
# Inspect the values
|
||||
exec bash -c 'bash -euo pipefail $WORK/inspect-value-files.sh 2>&1'
|
||||
cp stdout $WORK/inspect-value-files.txt
|
||||
|
||||
# Render the platform, capture stdout, and use update.sh to gate whether the
|
||||
# output file should be updated.
|
||||
exec bash -c 'bash -euo pipefail $WORK/render.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/render.txt
|
||||
|
||||
exec bash -c 'bash -euo pipefail $WORK/tree-deploy.sh 2>&1'
|
||||
cp stdout $WORK/tree-deploy.txt
|
||||
|
||||
# Make a commit
|
||||
exec git add .
|
||||
exec git commit -m '04-helm-component.txtar'
|
||||
|
||||
-- tree-deploy.sh --
|
||||
tree deploy
|
||||
-- render.sh --
|
||||
holos render platform
|
||||
-- update.sh --
|
||||
#! /bin/bash
|
||||
set -euo pipefail
|
||||
[[ -s "$1" ]] && [[ -z "${HOLOS_UPDATE_SCRIPTS:-}" ]] && exit 0
|
||||
cat > "$1"
|
||||
-- show-buildplans.sh --
|
||||
holos show buildplans
|
||||
-- inspect-value-files.sh --
|
||||
CUE_EXPERIMENT=embed holos cue export --out=yaml \
|
||||
./components/my-chart \
|
||||
-e valueFiles
|
||||
-- component-my-chart-header.sh --
|
||||
cat <<'EOF' > components/my-chart/my-chart.cue
|
||||
-- component-my-chart-trailer.sh --
|
||||
EOF
|
||||
-- component-my-chart.cue --
|
||||
@extern(embed)
|
||||
package holos
|
||||
|
||||
import "holos.example/config/environments"
|
||||
|
||||
parameters: {
|
||||
environments.#Config & {
|
||||
env: _ @tag(env)
|
||||
region: _ @tag(region)
|
||||
type: _ @tag(type)
|
||||
version: _ @tag(version)
|
||||
chart: _ @tag(chart)
|
||||
}
|
||||
}
|
||||
|
||||
// component represents the holos component definition, which produces a
|
||||
// BuildPlan for holos to execute, rendering the manifests.
|
||||
component: #Helm & {
|
||||
Chart: {
|
||||
// Migrated from https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L25
|
||||
version: parameters.chart
|
||||
repository: {
|
||||
name: "multi-sources-example"
|
||||
// Migrated from https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L23
|
||||
url: "https://kostis-codefresh.github.io/multi-sources-example"
|
||||
}
|
||||
}
|
||||
|
||||
// Migrated from https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L40
|
||||
// We use kustomize to manage the namespace, similar to how the original
|
||||
// article uses the ApplicationSet template to specify the namespace.
|
||||
KustomizeConfig: Kustomization: namespace: parameters.env
|
||||
|
||||
// Migrate the Helm Hierarchy preserving the behavior of over writing values.
|
||||
// Migrated from [valueFiles]. Later files win.
|
||||
//
|
||||
// [valueFiles]: https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L27-L32
|
||||
ValueFiles: [{
|
||||
name: "common-values.yaml"
|
||||
values: valueFiles["my-values/common-values.yaml"]
|
||||
}, {
|
||||
name: "version-values.yaml"
|
||||
values: valueFiles["my-values/app-version/\(parameters.version)-values.yaml"]
|
||||
}, {
|
||||
name: "type-values.yaml"
|
||||
values: valueFiles["my-values/env-type/\(parameters.type)-values.yaml"]
|
||||
}, {
|
||||
name: "region-values.yaml"
|
||||
values: valueFiles["my-values/regions/\(parameters.region)-values.yaml"]
|
||||
}, {
|
||||
name: "env-values.yaml"
|
||||
values: valueFiles["my-values/envs/\(parameters.env)-values.yaml"]
|
||||
}]
|
||||
}
|
||||
|
||||
// holos represents the output for the holos command line to process. The holos
|
||||
// command line processes a BuildPlan to render the helm chart component.
|
||||
//
|
||||
// Use the holos show buildplans command to see the BuildPlans that holos render
|
||||
// platform renders.
|
||||
holos: component.BuildPlan
|
||||
|
||||
// Migrated from https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L27-L32
|
||||
valueFiles: _ @embed(glob=my-values/*.yaml)
|
||||
valueFiles: _ @embed(glob=my-values/*/*-values.yaml)
|
||||
-- holos-show-platform.sh --
|
||||
holos show platform
|
||||
-- componentconfig-header.sh --
|
||||
cat <<'EOF' > components/componentconfig.cue
|
||||
-- componentconfig-trailer.sh --
|
||||
EOF
|
||||
-- componentconfig.cue --
|
||||
package holos
|
||||
|
||||
#ComponentConfig: {
|
||||
// Inject the output base directory from platform component parameters.
|
||||
OutputBaseDir: string | *"" @tag(outputBaseDir, type=string)
|
||||
}
|
||||
-- platform-my-chart-header.sh --
|
||||
cat <<'EOF' > platform/my-chart.cue
|
||||
-- platform-my-chart-trailer.sh --
|
||||
EOF
|
||||
-- platform-my-chart.cue --
|
||||
package holos
|
||||
|
||||
// Imports ./config/environments/*.cue as the environments cue package. The
|
||||
// package exposes ./config/environments/**/config.json files via the
|
||||
// environments.config struct
|
||||
import "holos.example/config/environments"
|
||||
|
||||
// Manage my-chart for each of the three environments. Platform components are
|
||||
// rendered by the holos render platform command.
|
||||
//
|
||||
// Use the following command command to inspect the Platform spec holos render
|
||||
// platform processes.
|
||||
//
|
||||
// holos show platform
|
||||
//
|
||||
// CONFIG represents each migrated environments/**/config.json file.
|
||||
for CONFIG in environments.config {
|
||||
// Add one holos component for each config.json file to the
|
||||
// Platform.spec.components list.
|
||||
Platform: Components: "\(CONFIG.env)-my-chart": #MyChart & {
|
||||
parameters: {
|
||||
env: CONFIG.env
|
||||
region: CONFIG.region
|
||||
type: CONFIG.type
|
||||
version: CONFIG.version
|
||||
chart: CONFIG.chart
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #MyChart defines a re-usable way to manage my-chart across qa, staging, and
|
||||
// production.
|
||||
#MyChart: {
|
||||
name: "my-chart"
|
||||
path: "components/my-chart"
|
||||
// CUE supports constraints, here we constrain environment to one of three
|
||||
// possible values.
|
||||
parameters: {
|
||||
// Embed the config.json schema (env, region, type, version, chart fields)
|
||||
environments.#Config
|
||||
|
||||
// Define the env field here as any value (_) so we can refer to it.
|
||||
// Otherwise cue complains #MyChart.parameters.outputBaseDir: reference
|
||||
// "env" not found
|
||||
env: _
|
||||
// Write output manifests organized by environment env in this case refers
|
||||
// to the env field defined by environments.#Config
|
||||
outputBaseDir: "environments/\(env)"
|
||||
}
|
||||
// CUE supports string substitution.
|
||||
annotations: "app.holos.run/description": "my-chart \(parameters.chart) for environment \(parameters.env)"
|
||||
// Selector labels
|
||||
labels: env: parameters.env
|
||||
}
|
||||
142
doc/website/blog/_migrate_appset/examples/05-application.txtar
Normal file
142
doc/website/blog/_migrate_appset/examples/05-application.txtar
Normal file
@@ -0,0 +1,142 @@
|
||||
# Work in the root of the example repo
|
||||
cd ../script-01-clone/multi-sources-example
|
||||
chmod 0755 $WORK/update.sh
|
||||
|
||||
# d9125b8 compose argocd application resources into every component
|
||||
exec cat $WORK/componentconfig-gitops-header.sh $WORK/componentconfig-gitops.cue $WORK/componentconfig-gitops-trailer.sh
|
||||
stdin stdout
|
||||
exec bash -xeuo pipefail
|
||||
exec diff components/componentconfig-gitops.cue $WORK/componentconfig-gitops.cue
|
||||
|
||||
# Render the platform, capture stdout, and use update.sh to gate whether the
|
||||
# output file should be updated.
|
||||
exec bash -c 'bash -euo pipefail $WORK/render.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/render.txt
|
||||
|
||||
exec bash -c 'bash -euo pipefail $WORK/tree-deploy.sh 2>&1'
|
||||
cp stdout $WORK/tree-deploy.txt
|
||||
|
||||
# Show an example application
|
||||
exec bash -c 'cat $(<$WORK/app.path)'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/app.yaml
|
||||
|
||||
# Show an example manifest
|
||||
exec bash -c 'cat $(<$WORK/manifest.path)'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/manifest.yaml
|
||||
|
||||
# Make a commit
|
||||
exec git add .
|
||||
exec git commit -m '05-application.txtar'
|
||||
exec git branch end-of-part-1
|
||||
|
||||
-- manifest.path --
|
||||
deploy/environments/prod-us/components/my-chart/my-chart.gen.yaml
|
||||
-- app.path --
|
||||
deploy/gitops/prod-us-my-chart-application.gen.yaml
|
||||
-- tree-deploy.sh --
|
||||
tree deploy
|
||||
-- render.sh --
|
||||
holos render platform
|
||||
-- update.sh --
|
||||
#! /bin/bash
|
||||
set -euo pipefail
|
||||
[[ -s "$1" ]] && [[ -z "${HOLOS_UPDATE_SCRIPTS:-}" ]] && exit 0
|
||||
cat > "$1"
|
||||
-- componentconfig-gitops-header.sh --
|
||||
cat <<'EOF' > components/componentconfig-gitops.cue
|
||||
-- componentconfig-gitops-trailer.sh --
|
||||
EOF
|
||||
-- componentconfig-gitops.cue --
|
||||
package holos
|
||||
|
||||
import (
|
||||
"path"
|
||||
app "argoproj.io/application/v1alpha1"
|
||||
)
|
||||
|
||||
parameters: {
|
||||
env: string @tag(env)
|
||||
}
|
||||
|
||||
// #ComponentConfig composes configuration into every Holos Component. Here we
|
||||
// compose an ArgoCD Application resource along side each component to reconcile
|
||||
// the hydrated manifests with the cluster.
|
||||
#ComponentConfig: {
|
||||
Name: _
|
||||
OutputBaseDir: _
|
||||
// Application resources are Environment scoped. Note the combination of
|
||||
// component name and environment must be unique.
|
||||
_ArgoAppName: "\(parameters.env)-\(Name)"
|
||||
|
||||
// Allow other aspects of the platform configuration to refer to
|
||||
// `Component._ArgoApplication` to get a handle on the Application resource
|
||||
// and mix additional configuration in.
|
||||
_ArgoApplication: app.#Application & {
|
||||
metadata: name: _ArgoAppName
|
||||
metadata: namespace: "argocd"
|
||||
metadata: labels: Labels
|
||||
// Label the Application with the env so we can easily filter in the UI.
|
||||
metadata: labels: env: parameters.env
|
||||
spec: {
|
||||
// Migrated from https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L40
|
||||
destination: server: "https://kubernetes.default.svc"
|
||||
destination: namespace: parameters.env
|
||||
project: "default"
|
||||
// source migrated from sources
|
||||
// https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L22-L35
|
||||
source: {
|
||||
path: string | *ResourcesPath
|
||||
repoURL: "https://github.com/holos-run/multi-sources-example"
|
||||
targetRevision: string | *"main"
|
||||
}
|
||||
// Migrated from https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L43-L48
|
||||
syncPolicy: syncOptions: ["CreateNamespace=true"]
|
||||
syncPolicy: automated: prune: true
|
||||
syncPolicy: automated: selfHeal: true
|
||||
}
|
||||
}
|
||||
|
||||
// We combine all Application resources into the deploy/gitops/ folder
|
||||
// assuming Application.metadata.name is unique. This makes it easy to apply
|
||||
// all of the hydrated Application resources in one shot.
|
||||
let ArtifactPath = path.Join(["gitops", "\(_ArgoApplication.metadata.name)-application.gen.yaml"], path.Unix)
|
||||
// Alternatively we could write the Applications along side the OutputBaseDir
|
||||
// let ArtifactPath = path.Join([OutputBaseDir, "gitops", "\(Name)-application.gen.yaml"], path.Unix)
|
||||
|
||||
// ResourcesPath represents the configuration path the Application is
|
||||
// configured to read as a source. This path contains the fully rendered
|
||||
// manifests produced by Holos and written to the GitOps repo.
|
||||
//
|
||||
// For example, to reconcile my-chart.gen.yaml for prod-us:
|
||||
// let ResourcesPath = "deploy/environments/prod-us/components/my-chart"
|
||||
let ResourcesPath = path.Join(["deploy", OutputBaseDir, "components", Name], path.Unix)
|
||||
|
||||
// Add the argocd Application instance label to GitOps so resources are in sync.
|
||||
// This is an example of how Holos makes it easy to add common labels to all
|
||||
// resources regardless of if they come from Helm, CUE, Kustomize, plain YAML
|
||||
// manifests, etc...
|
||||
KustomizeConfig: CommonLabels: "argocd.argoproj.io/instance": _ArgoAppName
|
||||
|
||||
// Labels for the Application itself. We filter the argocd application
|
||||
// instance label so ArgoCD doesn't think the Application resource manages
|
||||
// itself.
|
||||
let Labels = {
|
||||
for k, v in KustomizeConfig.CommonLabels {
|
||||
if k != "argocd.argoproj.io/instance" {
|
||||
(k): v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Artifacts: "\(Name)-application": {
|
||||
artifact: ArtifactPath
|
||||
generators: [{
|
||||
kind: "Resources"
|
||||
output: artifact
|
||||
resources: Application: (_ArgoAppName): _ArgoApplication
|
||||
}]
|
||||
}
|
||||
}
|
||||
198
doc/website/blog/_migrate_appset/examples/06-unification.txtar
Normal file
198
doc/website/blog/_migrate_appset/examples/06-unification.txtar
Normal file
@@ -0,0 +1,198 @@
|
||||
# Work in the root of the example repo
|
||||
cd ../script-01-clone/multi-sources-example
|
||||
chmod 0755 $WORK/update.sh
|
||||
|
||||
# Grep for replicas: to see what's used
|
||||
exec bash -c 'bash -euo pipefail $WORK/grep.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/grep.txt
|
||||
|
||||
# Common values
|
||||
exec bash -c 'cp $(<$WORK/common.path) $WORK/common.yaml'
|
||||
|
||||
# Remove common values
|
||||
exec cat $WORK/remove.head $WORK/remove.body $WORK/remove.tail
|
||||
stdin stdout
|
||||
exec bash -xeuo pipefail
|
||||
cp stdout $WORK/remove.out
|
||||
|
||||
# Render platform
|
||||
exec bash -c 'bash -euo pipefail $WORK/render.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/render.txt
|
||||
|
||||
# Check results
|
||||
exec bash -c 'bash -euo pipefail $WORK/check.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/check.txt
|
||||
|
||||
# Commit 1
|
||||
exec bash -c 'bash -euo pipefail $WORK/commit1.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/commit1.txt
|
||||
|
||||
# Unification Section
|
||||
# Unify Values
|
||||
exec cat $WORK/unify.head $WORK/unify.body $WORK/unify.tail
|
||||
stdin stdout
|
||||
exec bash -xeuo pipefail
|
||||
cp stdout $WORK/unify.out
|
||||
|
||||
# Render the platform to see the conflicts
|
||||
! exec bash -euo pipefail -c 'bash -euo pipefail $WORK/render2.sh 2>&1 | sed s,$(pwd)/,,g'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/render2.txt
|
||||
|
||||
# Copy the value files to show the conflict
|
||||
mkdir $WORK/conflict
|
||||
cp components/my-chart/my-values/env-type/prod-values.yaml $WORK/conflict/prod-values.yaml
|
||||
cp components/my-chart/my-values/envs/prod-eu-values.yaml $WORK/conflict/prod-eu-values.yaml
|
||||
|
||||
# Commit 2
|
||||
exec bash -c 'bash -euo pipefail $WORK/commit2.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/commit2.txt
|
||||
|
||||
# Remove replicaCount: 5 from env-type/prod-values.yaml
|
||||
exec cat $WORK/unify2.head $WORK/unify2.body $WORK/unify2.tail
|
||||
stdin stdout
|
||||
exec bash -xeuo pipefail
|
||||
cp stdout $WORK/unify2.out
|
||||
|
||||
# Render the platform again: no conflicts
|
||||
exec bash -euo pipefail -c 'bash -euo pipefail $WORK/render3.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/render3.txt
|
||||
|
||||
# Verify there are no changes to the deploy directory
|
||||
exec bash -c 'bash -euo pipefail $WORK/status3.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/status3.txt
|
||||
|
||||
# Commit 3
|
||||
exec bash -c 'bash -euo pipefail $WORK/commit3.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/commit3.txt
|
||||
|
||||
# Final wrap up - show build plan
|
||||
exec bash -c 'bash -euo pipefail $WORK/buildplan.sh 2>&1'
|
||||
cp stdout $WORK/buildplan.txt
|
||||
|
||||
-- buildplan.sh --
|
||||
holos show buildplans --selector env=prod-us
|
||||
-- status3.sh --
|
||||
git status deploy
|
||||
-- render3.sh --
|
||||
rm -rf deploy
|
||||
holos render platform
|
||||
-- unify2.head --
|
||||
patch -p1 <<'EOF'
|
||||
-- unify2.tail --
|
||||
EOF
|
||||
-- unify2.body --
|
||||
diff --git a/components/my-chart/my-values/env-type/prod-values.yaml b/components/my-chart/my-values/env-type/prod-values.yaml
|
||||
index fef58f4..a3cce4a 100644
|
||||
--- a/components/my-chart/my-values/env-type/prod-values.yaml
|
||||
+++ b/components/my-chart/my-values/env-type/prod-values.yaml
|
||||
@@ -1,11 +1,5 @@
|
||||
-# Kubernetes settings
|
||||
-replicaCount: 5
|
||||
-
|
||||
# Environment settings
|
||||
environmentType: production
|
||||
paypalUrl: "production.paypal.com"
|
||||
dbUser: "prod_username"
|
||||
dbPassword: "prod_password"
|
||||
-
|
||||
-
|
||||
-
|
||||
-- render2.sh --
|
||||
holos render platform --concurrency 1
|
||||
-- unify.head --
|
||||
patch -p1 <<'EOF'
|
||||
-- unify.tail --
|
||||
EOF
|
||||
-- unify.body --
|
||||
diff --git a/components/my-chart/my-chart.cue b/components/my-chart/my-chart.cue
|
||||
index cf1dae4..4f01828 100644
|
||||
--- a/components/my-chart/my-chart.cue
|
||||
+++ b/components/my-chart/my-chart.cue
|
||||
@@ -31,23 +31,14 @@ component: #Helm & {
|
||||
// article uses the ApplicationSet template to specify the namespace.
|
||||
KustomizeConfig: Kustomization: namespace: parameters.env
|
||||
|
||||
- // Migrate the Helm Hierarchy preserving the behavior of over writing values.
|
||||
- // Migrated from [valueFiles]. Later files win.
|
||||
+ // Migrate the Helm Hierarchy to CUE. Conflicts are errors.
|
||||
+ // Migrated from [valueFiles].
|
||||
//
|
||||
// [valueFiles]: https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L27-L32
|
||||
- ValueFiles: [{
|
||||
- name: "version-values.yaml"
|
||||
- values: valueFiles["my-values/app-version/\(parameters.version)-values.yaml"]
|
||||
- }, {
|
||||
- name: "type-values.yaml"
|
||||
- values: valueFiles["my-values/env-type/\(parameters.type)-values.yaml"]
|
||||
- }, {
|
||||
- name: "region-values.yaml"
|
||||
- values: valueFiles["my-values/regions/\(parameters.region)-values.yaml"]
|
||||
- }, {
|
||||
- name: "env-values.yaml"
|
||||
- values: valueFiles["my-values/envs/\(parameters.env)-values.yaml"]
|
||||
- }]
|
||||
+ Values: valueFiles["my-values/app-version/\(parameters.version)-values.yaml"]
|
||||
+ Values: valueFiles["my-values/env-type/\(parameters.type)-values.yaml"]
|
||||
+ Values: valueFiles["my-values/regions/\(parameters.region)-values.yaml"]
|
||||
+ Values: valueFiles["my-values/envs/\(parameters.env)-values.yaml"]
|
||||
}
|
||||
|
||||
// holos represents the output for the holos command line to process. The holos
|
||||
-- commit3.sh --
|
||||
git add .
|
||||
git commit -m '06-unification step 3'
|
||||
-- commit2.sh --
|
||||
git add .
|
||||
git commit -m '06-unification step 2'
|
||||
-- commit1.sh --
|
||||
git add .
|
||||
git commit -m '06-unification step 1'
|
||||
-- render.sh --
|
||||
rm -rf deploy
|
||||
holos render platform
|
||||
-- check.sh --
|
||||
git status deploy
|
||||
-- remove.head --
|
||||
git rm -f components/my-chart/my-values/common-values.yaml
|
||||
patch -p1 <<'EOF'
|
||||
-- remove.tail --
|
||||
EOF
|
||||
-- remove.body --
|
||||
diff --git a/components/my-chart/my-chart.cue b/components/my-chart/my-chart.cue
|
||||
index 2809d1a..cf1dae4 100644
|
||||
--- a/components/my-chart/my-chart.cue
|
||||
+++ b/components/my-chart/my-chart.cue
|
||||
@@ -36,9 +36,6 @@ component: #Helm & {
|
||||
//
|
||||
// [valueFiles]: https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L27-L32
|
||||
ValueFiles: [{
|
||||
- name: "common-values.yaml"
|
||||
- values: valueFiles["my-values/common-values.yaml"]
|
||||
- }, {
|
||||
name: "version-values.yaml"
|
||||
values: valueFiles["my-values/app-version/\(parameters.version)-values.yaml"]
|
||||
}, {
|
||||
@@ -61,5 +58,4 @@ component: #Helm & {
|
||||
holos: component.BuildPlan
|
||||
|
||||
// Migrated from https://github.com/holos-run/multi-sources-example/blob/v0.1.0/appsets/4-final/all-my-envs-appset-with-version.yaml#L27-L32
|
||||
-valueFiles: _ @embed(glob=my-values/*.yaml)
|
||||
valueFiles: _ @embed(glob=my-values/*/*-values.yaml)
|
||||
-- common.path --
|
||||
components/my-chart/my-values/common-values.yaml
|
||||
-- grep.sh --
|
||||
grep -r replicas: deploy
|
||||
-- update.sh --
|
||||
#! /bin/bash
|
||||
set -euo pipefail
|
||||
[[ -s "$1" ]] && [[ -z "${HOLOS_UPDATE_SCRIPTS:-}" ]] && exit 0
|
||||
cat > "$1"
|
||||
@@ -0,0 +1,5 @@
|
||||
# Work in the root of the example repo
|
||||
cd ../script-01-clone/multi-sources-example
|
||||
|
||||
# Checkout the v0.4.x branch
|
||||
exec git checkout v0.4.x
|
||||
@@ -0,0 +1,77 @@
|
||||
# Work in the root of the example repo
|
||||
cd ../script-01-clone/multi-sources-example
|
||||
env HOME=$WORK/.tmp
|
||||
env CUSTOMER=customer-zzsbbmfc
|
||||
chmod 0755 $WORK/update.sh
|
||||
|
||||
mkdir $WORK/files
|
||||
|
||||
# Reset
|
||||
exec git reset --hard origin/v0.4.x
|
||||
|
||||
# Values Generator
|
||||
exec bash -c 'bash -euo pipefail $WORK/generator.sh 2>&1'
|
||||
# Show the generated structure
|
||||
exec bash -c 'bash -eu $WORK/show-deployment-configs.sh 2>&1'
|
||||
cp stdout $WORK/show-deployment-configs.txt
|
||||
exec bash -c 'bash -euo pipefail $WORK/show-value-files.sh 2>&1'
|
||||
cp stdout $WORK/show-value-files.txt
|
||||
|
||||
# Show one buildplan
|
||||
exec bash -c 'bash -euo pipefail $WORK/show-one-buildplan.sh 2>&1'
|
||||
cp stdout $WORK/show-one-buildplan.txt
|
||||
|
||||
# Render one customer
|
||||
exec bash -c 'bash -euo pipefail $WORK/render-one-customer.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/render-one-customer.txt
|
||||
|
||||
# Add and commit
|
||||
exec bash -c 'bash -euo pipefail $WORK/add-and-commit-step1.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/add-and-commit-step1.txt
|
||||
|
||||
# Flattening Values
|
||||
exec bash -c 'bash -euo pipefail $WORK/render-step2.sh 2>&1'
|
||||
stdin stdout
|
||||
exec $WORK/update.sh $WORK/render-step2.txt
|
||||
|
||||
# Show the flattened values
|
||||
exec bash -c 'bash -euo pipefail $WORK/show-flattened-values.sh 2>&1'
|
||||
cp stdout $WORK/show-flattened-values.txt
|
||||
|
||||
# Step 2 - Take a look at the following files
|
||||
cp platform/my-app.cue $WORK/files/platform-my-app.cue
|
||||
cp platform/render-values.cue $WORK/files/platform-render-values.cue
|
||||
|
||||
-- show-flattened-values.sh --
|
||||
# Flattened values written here
|
||||
tree deploy/values/customers/$CUSTOMER
|
||||
# Note how the input deployment configs now map 1:1 to values files.
|
||||
tree config/my-app/deployment/customers/$CUSTOMER
|
||||
-- render-step2.sh --
|
||||
holos render platform -t flatten -t step2 \
|
||||
--selector customer=$CUSTOMER
|
||||
-- add-and-commit-step1.sh --
|
||||
git add deploy
|
||||
git commit -m 'render step1 with helm value file overrides'
|
||||
-- render-one-customer.sh --
|
||||
holos render platform -t flatten -t step1 \
|
||||
--selector customer=$CUSTOMER
|
||||
-- show-one-buildplan.sh --
|
||||
holos show buildplans -t flatten -t step1 \
|
||||
--selector customer=$CUSTOMER,cluster=prod9-management
|
||||
-- show-deployment-configs.sh --
|
||||
ls config/my-app/deployment/customers/*/clusters/*/config.json | wc -l
|
||||
-- show-value-files.sh --
|
||||
tree -d components/my-app/values/
|
||||
tree components/my-app/values/05-environments
|
||||
tree components/my-app/values/06-tiers
|
||||
tree components/my-app/values/07-scopes
|
||||
-- generator.sh --
|
||||
go run ./generator
|
||||
-- update.sh --
|
||||
#! /bin/bash
|
||||
set -euo pipefail
|
||||
[[ -s "$1" ]] && [[ -z "${HOLOS_UPDATE_SCRIPTS:-}" ]] && exit 0
|
||||
cat > "$1"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user