diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 88e9a3e650..52b3d38b31 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,12 @@ jobs: - name: Install tools uses: ./.github/actions/install-tools + - name: install grype + env: + VERSION: v0.74.6 + run: "curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin $VERSION" + shell: bash + - name: Build CLI run: | make build-cli-linux-amd diff --git a/.grype.yaml b/.grype.yaml new file mode 100644 index 0000000000..e5c8be63c8 --- /dev/null +++ b/.grype.yaml @@ -0,0 +1,4 @@ +ignore: + # From helm - This behavior was introduced intentionally, and cannot be removed without breaking backwards compatibility (some users may be relying on these values). + # https://helm.sh/blog/response-cve-2019-25210/ + - vulnerability: GHSA-jw44-4f3j-q396 diff --git a/go.mod b/go.mod index eda6a176ee..97646e3849 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,6 @@ module github.com/zarf-dev/zarf go 1.22.4 -replace github.com/zarf-dev/zarf/src/api => ./src/api - // TODO (@AABRO): Pending merge into github.com/gojsonschema/gojsonschema (https://github.com/gojsonschema/gojsonschema/pull/5) replace github.com/xeipuuv/gojsonschema => github.com/defenseunicorns/gojsonschema v0.0.0-20231116163348-e00f069122d6 @@ -31,7 +29,7 @@ require ( github.com/go-git/go-git/v5 v5.11.0 github.com/goccy/go-yaml v1.12.0 github.com/gofrs/flock v0.8.1 - github.com/google/go-containerregistry v0.20.1 + github.com/google/go-containerregistry v0.20.2 github.com/gosuri/uitable v0.0.4 github.com/invopop/jsonschema v0.12.0 github.com/mholt/archiver/v3 v3.5.1 @@ -39,7 +37,7 @@ require ( github.com/opencontainers/image-spec v1.1.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.18.0 - github.com/pterm/pterm v0.12.78 + github.com/pterm/pterm v0.12.79 github.com/sergi/go-diff v1.3.1 github.com/sigstore/cosign/v2 v2.2.3 github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.1 @@ -51,7 +49,6 @@ require ( github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 github.com/xeipuuv/gojsonschema v1.2.0 - github.com/zarf-dev/zarf/src/api v0.0.0-00010101000000-000000000000 golang.org/x/crypto v0.25.0 golang.org/x/sync v0.7.0 golang.org/x/term v0.22.0 @@ -64,8 +61,8 @@ require ( k8s.io/kubectl v0.30.0 oras.land/oras-go/v2 v2.5.0 sigs.k8s.io/cli-utils v0.36.0 - sigs.k8s.io/kustomize/api v0.16.0 - sigs.k8s.io/kustomize/kyaml v0.16.0 + sigs.k8s.io/kustomize/api v0.17.3 + sigs.k8s.io/kustomize/kyaml v0.17.2 sigs.k8s.io/yaml v1.4.0 ) @@ -74,6 +71,7 @@ require ( github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/gofrs/uuid v4.2.0+incompatible // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect ) require ( @@ -220,7 +218,7 @@ require ( github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/docker/cli v26.0.0+incompatible // indirect + github.com/docker/cli v27.1.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker v25.0.6+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.0 // indirect @@ -502,7 +500,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d // indirect google.golang.org/grpc v1.64.1 // indirect google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/evanphx/json-patch.v5 v5.6.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 // indirect diff --git a/go.sum b/go.sum index a8b3b34e62..3821f05f82 100644 --- a/go.sum +++ b/go.sum @@ -635,8 +635,8 @@ github.com/distribution/distribution/v3 v3.0.0-alpha.1 h1:jn7I1gvjOvmLztH1+1cLiU github.com/distribution/distribution/v3 v3.0.0-alpha.1/go.mod h1:LCp4JZp1ZalYg0W/TN05jarCQu+h4w7xc7ZfQF4Y/cY= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v26.0.0+incompatible h1:90BKrx1a1HKYpSnnBFR6AgDq/FqkHxwlUyzJVPxD30I= -github.com/docker/cli v26.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= +github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= @@ -929,8 +929,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.20.1 h1:eTgx9QNYugV4DN5mz4U8hiAGTi1ybXn0TPi4Smd8du0= -github.com/google/go-containerregistry v0.20.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= +github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= +github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLNb9x9cg= github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -1471,8 +1471,8 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= -github.com/pterm/pterm v0.12.78 h1:QTWKaIAa4B32GKwqVXtu9m1DUMgWw3VRljMkMevX+b8= -github.com/pterm/pterm v0.12.78/go.mod h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IGaQAAo= +github.com/pterm/pterm v0.12.79 h1:lH3yrYMhdpeqX9y5Ep1u7DejyHy7NSQg9qrBjF9dFT4= +github.com/pterm/pterm v0.12.79/go.mod h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IGaQAAo= github.com/rakyll/hey v0.1.4 h1:hhc8GIqHN4+rPFZvkM9lkCQGi7da0sINM83xxpFkbPA= github.com/rakyll/hey v0.1.4/go.mod h1:nAOTOo+L52KB9SZq/M6J18kxjto4yVtXQDjU2HgjUPI= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= @@ -2422,8 +2422,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/evanphx/json-patch.v5 v5.6.0 h1:BMT6KIwBD9CaU91PJCZIe46bDmBWa9ynTQgJIOpfQBk= -gopkg.in/evanphx/json-patch.v5 v5.6.0/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -2510,12 +2510,12 @@ sigs.k8s.io/controller-runtime v0.18.1 h1:RpWbigmuiylbxOCLy0tGnq1cU1qWPwNIQzoJk+ sigs.k8s.io/controller-runtime v0.18.1/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.16.0 h1:/zAR4FOQDCkgSDmVzV2uiFbuy9bhu3jEzthrHCuvm1g= -sigs.k8s.io/kustomize/api v0.16.0/go.mod h1:MnFZ7IP2YqVyVwMWoRxPtgl/5hpA+eCCrQR/866cm5c= +sigs.k8s.io/kustomize/api v0.17.3 h1:6GCuHSsxq7fN5yhF2XrC+AAr8gxQwhexgHflOAD/JJU= +sigs.k8s.io/kustomize/api v0.17.3/go.mod h1:TuDH4mdx7jTfK61SQ/j1QZM/QWR+5rmEiNjvYlhzFhc= sigs.k8s.io/kustomize/kustomize/v5 v5.0.4-0.20230601165947-6ce0bf390ce3 h1:vq2TtoDcQomhy7OxXLUOzSbHMuMYq0Bjn93cDtJEdKw= sigs.k8s.io/kustomize/kustomize/v5 v5.0.4-0.20230601165947-6ce0bf390ce3/go.mod h1:/d88dHCvoy7d0AKFT0yytezSGZKjsZBVs9YTkBHSGFk= -sigs.k8s.io/kustomize/kyaml v0.16.0 h1:6J33uKSoATlKZH16unr2XOhDI+otoe2sR3M8PDzW3K0= -sigs.k8s.io/kustomize/kyaml v0.16.0/go.mod h1:xOK/7i+vmE14N2FdFyugIshB8eF6ALpy7jI87Q2nRh4= +sigs.k8s.io/kustomize/kyaml v0.17.2 h1:+AzvoJUY0kq4QAhH/ydPHHMRLijtUKiyVyh7fOSshr0= +sigs.k8s.io/kustomize/kyaml v0.17.2/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= sigs.k8s.io/release-utils v0.7.7 h1:JKDOvhCk6zW8ipEOkpTGDH/mW3TI+XqtPp16aaQ79FU= sigs.k8s.io/release-utils v0.7.7/go.mod h1:iU7DGVNi3umZJ8q6aHyUFzsDUIaYwNnNKGHo3YE5E3s= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/src/api/go.mod b/src/api/go.mod deleted file mode 100644 index 9324efd1bb..0000000000 --- a/src/api/go.mod +++ /dev/null @@ -1,41 +0,0 @@ -module github.com/zarf-dev/zarf/src/api - -go 1.22.4 - -replace github.com/zarf-dev/zarf => ../.. - -require ( - github.com/defenseunicorns/pkg/helpers/v2 v2.0.1 - github.com/invopop/jsonschema v0.12.0 - github.com/stretchr/testify v1.9.0 - github.com/zarf-dev/zarf v0.37.0 - k8s.io/apimachinery v0.30.3 -) - -require ( - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/buger/jsonparser v1.1.1 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/otiai10/copy v1.14.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect - golang.org/x/net v0.27.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/utils v0.0.0-20231127182322-b307cd553661 // indirect - oras.land/oras-go/v2 v2.5.0 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect -) diff --git a/src/api/go.sum b/src/api/go.sum deleted file mode 100644 index 013cc9ae02..0000000000 --- a/src/api/go.sum +++ /dev/null @@ -1,107 +0,0 @@ -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/defenseunicorns/pkg/helpers/v2 v2.0.1 h1:j08rz9vhyD9Bs+yKiyQMY2tSSejXRMxTqEObZ5M1Wbk= -github.com/defenseunicorns/pkg/helpers/v2 v2.0.1/go.mod h1:u1PAqOICZyiGIVA2v28g55bQH1GiAt0Bc4U9/rnWQvQ= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= -github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= -github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= -github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= -github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc= -k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= -k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI= -k8s.io/utils v0.0.0-20231127182322-b307cd553661/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c= -oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/src/api/v1alpha1/component.go b/src/api/v1alpha1/component.go index bd798c82f6..9739de8644 100644 --- a/src/api/v1alpha1/component.go +++ b/src/api/v1alpha1/component.go @@ -7,8 +7,6 @@ package v1alpha1 import ( "github.com/invopop/jsonschema" "github.com/zarf-dev/zarf/src/api/v1alpha1/extensions" - "github.com/zarf-dev/zarf/src/pkg/utils/exec" - "github.com/zarf-dev/zarf/src/pkg/variables" ) // ZarfComponent is the primary functional grouping of assets to deploy by Zarf. @@ -228,7 +226,7 @@ type ZarfComponentActionDefaults struct { // Additional environment variables for commands. Env []string `json:"env,omitempty"` // (cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems. - Shell exec.Shell `json:"shell,omitempty"` + Shell Shell `json:"shell,omitempty"` } // ZarfComponentAction represents a single action to run during a zarf package operation. @@ -246,11 +244,11 @@ type ZarfComponentAction struct { // The command to run. Must specify either cmd or wait for the action to do anything. Cmd string `json:"cmd,omitempty"` // (cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems. - Shell *exec.Shell `json:"shell,omitempty"` + Shell *Shell `json:"shell,omitempty"` // [Deprecated] (replaced by setVariables) (onDeploy/cmd only) The name of a variable to update with the output of the command. This variable will be available to all remaining actions and components in the package. This will be removed in Zarf v1.0.0. DeprecatedSetVariable string `json:"setVariable,omitempty" jsonschema:"pattern=^[A-Z0-9_]+$"` // (onDeploy/cmd only) An array of variables to update with the output of the command. These variables will be available to all remaining actions and components in the package. - SetVariables []variables.Variable `json:"setVariables,omitempty"` + SetVariables []Variable `json:"setVariables,omitempty"` // Description of the action to be displayed during package execution instead of the command. Description string `json:"description,omitempty"` // Wait for a condition to be met before continuing. Must specify either cmd or wait for the action. See the 'zarf tools wait-for' command for more info. @@ -331,3 +329,10 @@ func (ZarfComponentImport) JSONSchemaExtend(schema *jsonschema.Schema) { path.Not = notSchema url.Not = notSchema } + +// Shell represents the desired shell to use for a given command +type Shell struct { + Windows string `json:"windows,omitempty" jsonschema:"description=(default 'powershell') Indicates a preference for the shell to use on Windows systems (note that choosing 'cmd' will turn off migrations like touch -> New-Item),example=powershell,example=cmd,example=pwsh,example=sh,example=bash,example=gsh"` + Linux string `json:"linux,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on Linux systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"` + Darwin string `json:"darwin,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on macOS systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"` +} diff --git a/src/api/v1alpha1/package.go b/src/api/v1alpha1/package.go index ef81a3a96d..92beae17d1 100644 --- a/src/api/v1alpha1/package.go +++ b/src/api/v1alpha1/package.go @@ -5,7 +5,24 @@ package v1alpha1 import ( - "github.com/zarf-dev/zarf/src/pkg/variables" + "fmt" + "regexp" +) + +// VariableType represents a type of a Zarf package variable +type VariableType string + +const ( + // RawVariableType is the default type for a Zarf package variable + RawVariableType VariableType = "raw" + // FileVariableType is a type for a Zarf package variable that loads its contents from a file + FileVariableType VariableType = "file" +) + +var ( + // IsUppercaseNumberUnderscore is a regex for uppercase, numbers and underscores. + // https://regex101.com/r/tfsEuZ/1 + IsUppercaseNumberUnderscore = regexp.MustCompile(`^[A-Z0-9_]+$`).MatchString ) // ZarfPackageKind is an enum of the different kinds of Zarf packages. @@ -16,7 +33,8 @@ const ( ZarfInitConfig ZarfPackageKind = "ZarfInitConfig" // ZarfPackageConfig is the default kind of Zarf package, primarily used during `zarf package`. ZarfPackageConfig ZarfPackageKind = "ZarfPackageConfig" - APIVersion string = "zarf.dev/v1alpha1" + // APIVersion the api version of this package. + APIVersion string = "zarf.dev/v1alpha1" ) // ZarfPackage the top-level structure of a Zarf config file. @@ -32,9 +50,9 @@ type ZarfPackage struct { // List of components to deploy in this package. Components []ZarfComponent `json:"components" jsonschema:"minItems=1"` // Constant template values applied on deploy for K8s resources. - Constants []variables.Constant `json:"constants,omitempty"` + Constants []Constant `json:"constants,omitempty"` // Variable template values applied on deploy for K8s resources. - Variables []variables.InteractiveVariable `json:"variables,omitempty"` + Variables []InteractiveVariable `json:"variables,omitempty"` } // IsInitConfig returns whether a Zarf package is an init config. @@ -62,6 +80,60 @@ func (pkg ZarfPackage) IsSBOMAble() bool { return false } +// Variable represents a variable that has a value set programmatically +type Variable struct { + // The name to be used for the variable + Name string `json:"name" jsonschema:"pattern=^[A-Z0-9_]+$"` + // Whether to mark this variable as sensitive to not print it in the log + Sensitive bool `json:"sensitive,omitempty"` + // Whether to automatically indent the variable's value (if multiline) when templating. Based on the number of chars before the start of ###ZARF_VAR_. + AutoIndent bool `json:"autoIndent,omitempty"` + // An optional regex pattern that a variable value must match before a package deployment can continue. + Pattern string `json:"pattern,omitempty"` + // Changes the handling of a variable to load contents differently (i.e. from a file rather than as a raw variable - templated files should be kept below 1 MiB) + Type VariableType `json:"type,omitempty" jsonschema:"enum=raw,enum=file"` +} + +// InteractiveVariable is a variable that can be used to prompt a user for more information +type InteractiveVariable struct { + Variable `json:",inline"` + // A description of the variable to be used when prompting the user a value + Description string `json:"description,omitempty"` + // The default value to use for the variable + Default string `json:"default,omitempty"` + // Whether to prompt the user for input for this variable + Prompt bool `json:"prompt,omitempty"` +} + +// Constant are constants that can be used to dynamically template K8s resources or run in actions. +type Constant struct { + // The name to be used for the constant + Name string `json:"name" jsonschema:"pattern=^[A-Z0-9_]+$"` + // The value to set for the constant during deploy + Value string `json:"value"` + // A description of the constant to explain its purpose on package create or deploy confirmation prompts + Description string `json:"description,omitempty"` + // Whether to automatically indent the variable's value (if multiline) when templating. Based on the number of chars before the start of ###ZARF_CONST_. + AutoIndent bool `json:"autoIndent,omitempty"` + // An optional regex pattern that a constant value must match before a package can be created. + Pattern string `json:"pattern,omitempty"` +} + +// SetVariable tracks internal variables that have been set during this run of Zarf +type SetVariable struct { + Variable `json:",inline"` + // The value the variable is currently set with + Value string `json:"value"` +} + +// Validate runs all validation checks on a package constant. +func (c Constant) Validate() error { + if !regexp.MustCompile(c.Pattern).MatchString(c.Value) { + return fmt.Errorf("provided value for constant %s does not match pattern %s", c.Name, c.Pattern) + } + return nil +} + // ZarfMetadata lists information about the current ZarfPackage. type ZarfMetadata struct { // Name to identify this Zarf package. diff --git a/src/api/v1alpha1/validate.go b/src/api/v1alpha1/validate.go index 391e0f000d..f23be4769f 100644 --- a/src/api/v1alpha1/validate.go +++ b/src/api/v1alpha1/validate.go @@ -12,7 +12,6 @@ import ( "strings" "github.com/defenseunicorns/pkg/helpers/v2" - "github.com/zarf-dev/zarf/src/config/lang" "k8s.io/apimachinery/pkg/util/validation" ) @@ -48,16 +47,71 @@ const ( errChartReleaseNameEmpty = "release name empty, unable to fallback to chart name" ) +const ( + //nolint:revive //ignore + PkgValidateErrInitNoYOLO = "sorry, you can't YOLO an init package" + //nolint:revive //ignore + PkgValidateErrConstant = "invalid package constant: %w" + //nolint:revive //ignore + PkgValidateErrYOLONoOCI = "OCI images not allowed in YOLO" + //nolint:revive //ignore + PkgValidateErrYOLONoGit = "git repos not allowed in YOLO" + //nolint:revive //ignore + PkgValidateErrYOLONoArch = "cluster architecture not allowed in YOLO" + //nolint:revive //ignore + PkgValidateErrYOLONoDistro = "cluster distros not allowed in YOLO" + //nolint:revive //ignore + PkgValidateErrComponentNameNotUnique = "component name %q is not unique" + //nolint:revive //ignore + PkgValidateErrComponentReqDefault = "component %q cannot be both required and default" + //nolint:revive //ignore + PkgValidateErrComponentReqGrouped = "component %q cannot be both required and grouped" + //nolint:revive //ignore + PkgValidateErrChartNameNotUnique = "chart name %q is not unique" + //nolint:revive //ignore + PkgValidateErrChart = "invalid chart definition: %w" + //nolint:revive //ignore + PkgValidateErrManifestNameNotUnique = "manifest name %q is not unique" + //nolint:revive //ignore + PkgValidateErrManifest = "invalid manifest definition: %w" + //nolint:revive //ignore + PkgValidateErrGroupMultipleDefaults = "group %q has multiple defaults (%q, %q)" + //nolint:revive //ignore + PkgValidateErrGroupOneComponent = "group %q only has one component (%q)" + //nolint:revive //ignore + PkgValidateErrAction = "invalid action: %w" + //nolint:revive //ignore + PkgValidateErrActionCmdWait = "action %q cannot be both a command and wait action" + //nolint:revive //ignore + PkgValidateErrActionClusterNetwork = "a single wait action must contain only one of cluster or network" + //nolint:revive //ignore + PkgValidateErrChartName = "chart %q exceed the maximum length of %d characters" + //nolint:revive //ignore + PkgValidateErrChartNamespaceMissing = "chart %q must include a namespace" + //nolint:revive //ignore + PkgValidateErrChartURLOrPath = "chart %q must have either a url or localPath" + //nolint:revive //ignore + PkgValidateErrChartVersion = "chart %q must include a chart version" + //nolint:revive //ignore + PkgValidateErrImportDefinition = "invalid imported definition for %s: %s" + //nolint:revive //ignore + PkgValidateErrManifestFileOrKustomize = "manifest %q must have at least one file or kustomization" + //nolint:revive //ignore + PkgValidateErrManifestNameLength = "manifest %q exceed the maximum length of %d characters" + //nolint:revive //ignore + PkgValidateErrVariable = "invalid package variable: %w" +) + // Validate runs all validation checks on the package. func (pkg ZarfPackage) Validate() error { var err error if pkg.Kind == ZarfInitConfig && pkg.Metadata.YOLO { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrInitNoYOLO)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrInitNoYOLO)) } for _, constant := range pkg.Constants { if varErr := constant.Validate(); varErr != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrConstant, varErr)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrConstant, varErr)) } } @@ -68,19 +122,19 @@ func (pkg ZarfPackage) Validate() error { if pkg.Metadata.YOLO { for _, component := range pkg.Components { if len(component.Images) > 0 { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrYOLONoOCI)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoOCI)) } if len(component.Repos) > 0 { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrYOLONoGit)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoGit)) } if component.Only.Cluster.Architecture != "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrYOLONoArch)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoArch)) } if len(component.Only.Cluster.Distros) > 0 { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrYOLONoDistro)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoDistro)) } } } @@ -88,16 +142,16 @@ func (pkg ZarfPackage) Validate() error { for _, component := range pkg.Components { // ensure component name is unique if _, ok := uniqueComponentNames[component.Name]; ok { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrComponentNameNotUnique, component.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrComponentNameNotUnique, component.Name)) } uniqueComponentNames[component.Name] = true if component.IsRequired() { if component.Default { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrComponentReqDefault, component.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrComponentReqDefault, component.Name)) } if component.DeprecatedGroup != "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrComponentReqGrouped, component.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrComponentReqGrouped, component.Name)) } } @@ -105,12 +159,12 @@ func (pkg ZarfPackage) Validate() error { for _, chart := range component.Charts { // ensure chart name is unique if _, ok := uniqueChartNames[chart.Name]; ok { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartNameNotUnique, chart.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartNameNotUnique, chart.Name)) } uniqueChartNames[chart.Name] = true if chartErr := chart.Validate(); chartErr != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChart, chartErr)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChart, chartErr)) } } @@ -118,12 +172,12 @@ func (pkg ZarfPackage) Validate() error { for _, manifest := range component.Manifests { // ensure manifest name is unique if _, ok := uniqueManifestNames[manifest.Name]; ok { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrManifestNameNotUnique, manifest.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrManifestNameNotUnique, manifest.Name)) } uniqueManifestNames[manifest.Name] = true if manifestErr := manifest.Validate(); manifestErr != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrManifest, manifestErr)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrManifest, manifestErr)) } } @@ -135,7 +189,7 @@ func (pkg ZarfPackage) Validate() error { if component.DeprecatedGroup != "" { if component.Default { if _, ok := groupDefault[component.DeprecatedGroup]; ok { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrGroupMultipleDefaults, component.DeprecatedGroup, groupDefault[component.DeprecatedGroup], component.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrGroupMultipleDefaults, component.DeprecatedGroup, groupDefault[component.DeprecatedGroup], component.Name)) } groupDefault[component.DeprecatedGroup] = component.Name } @@ -145,7 +199,7 @@ func (pkg ZarfPackage) Validate() error { for groupKey, componentNames := range groupedComponents { if len(componentNames) == 1 { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrGroupOneComponent, groupKey, componentNames[0])) + err = errors.Join(err, fmt.Errorf(PkgValidateErrGroupOneComponent, groupKey, componentNames[0])) } } @@ -180,19 +234,19 @@ func (c ZarfComponent) Validate() error { // ensure path or url is provided if path == "" && url == "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrImportDefinition, c.Name, "neither a path nor a URL was provided")) + err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "neither a path nor a URL was provided")) } // ensure path and url are not both provided if path != "" && url != "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrImportDefinition, c.Name, "both a path and a URL were provided")) + err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "both a path and a URL were provided")) } // validation for path if url == "" && path != "" { // ensure path is not an absolute path if filepath.IsAbs(path) { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrImportDefinition, c.Name, "path cannot be an absolute path")) + err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "path cannot be an absolute path")) } } @@ -200,7 +254,7 @@ func (c ZarfComponent) Validate() error { if url != "" && path == "" { ok := helpers.IsOCIURL(url) if !ok { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrImportDefinition, c.Name, "URL is not a valid OCI URL")) + err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "URL is not a valid OCI URL")) } } @@ -227,7 +281,7 @@ func (as ZarfComponentActionSet) Validate() error { validate := func(actions []ZarfComponentAction) { for _, action := range actions { if actionErr := action.Validate(); actionErr != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrAction, actionErr)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrAction, actionErr)) } } } @@ -246,17 +300,17 @@ func (action ZarfComponentAction) Validate() error { if action.Wait != nil { // Validate only cmd or wait, not both if action.Cmd != "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrActionCmdWait, action.Cmd)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrActionCmdWait, action.Cmd)) } // Validate only cluster or network, not both if action.Wait.Cluster != nil && action.Wait.Network != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrActionClusterNetwork)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrActionClusterNetwork)) } // Validate at least one of cluster or network if action.Wait.Cluster == nil && action.Wait.Network == nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrActionClusterNetwork)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrActionClusterNetwork)) } } @@ -291,24 +345,24 @@ func (chart ZarfChart) Validate() error { var err error if len(chart.Name) > ZarfMaxChartNameLength { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartName, chart.Name, ZarfMaxChartNameLength)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartName, chart.Name, ZarfMaxChartNameLength)) } if chart.Namespace == "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartNamespaceMissing, chart.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartNamespaceMissing, chart.Name)) } // Must have a url or localPath (and not both) if chart.URL != "" && chart.LocalPath != "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartURLOrPath, chart.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartURLOrPath, chart.Name)) } if chart.URL == "" && chart.LocalPath == "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartURLOrPath, chart.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartURLOrPath, chart.Name)) } if chart.Version == "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartVersion, chart.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartVersion, chart.Name)) } if nameErr := validateReleaseName(chart.Name, chart.ReleaseName); nameErr != nil { @@ -323,11 +377,11 @@ func (manifest ZarfManifest) Validate() error { var err error if len(manifest.Name) > ZarfMaxChartNameLength { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrManifestNameLength, manifest.Name, ZarfMaxChartNameLength)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrManifestNameLength, manifest.Name, ZarfMaxChartNameLength)) } if len(manifest.Files) < 1 && len(manifest.Kustomizations) < 1 { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrManifestFileOrKustomize, manifest.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrManifestFileOrKustomize, manifest.Name)) } return err diff --git a/src/api/v1alpha1/validate_test.go b/src/api/v1alpha1/validate_test.go index de3f675c2e..b41a5c522a 100644 --- a/src/api/v1alpha1/validate_test.go +++ b/src/api/v1alpha1/validate_test.go @@ -12,8 +12,6 @@ import ( "github.com/defenseunicorns/pkg/helpers/v2" "github.com/stretchr/testify/require" - "github.com/zarf-dev/zarf/src/config/lang" - "github.com/zarf-dev/zarf/src/pkg/variables" ) func TestZarfPackageValidate(t *testing.T) { @@ -81,7 +79,7 @@ func TestZarfPackageValidate(t *testing.T) { Name: "duplicate", }, }, - Constants: []variables.Constant{ + Constants: []Constant{ { Name: "BAD", Pattern: "^good_val$", @@ -90,14 +88,14 @@ func TestZarfPackageValidate(t *testing.T) { }, }, expectedErrs: []string{ - fmt.Errorf(lang.PkgValidateErrConstant, fmt.Errorf(lang.PkgValidateErrPkgConstantPattern, "BAD", "^good_val$")).Error(), - fmt.Sprintf(lang.PkgValidateErrComponentReqDefault, "invalid"), - fmt.Sprintf(lang.PkgValidateErrChartNameNotUnique, "chart1"), - fmt.Sprintf(lang.PkgValidateErrManifestNameNotUnique, "manifest1"), - fmt.Sprintf(lang.PkgValidateErrComponentReqGrouped, "required-in-group"), - fmt.Sprintf(lang.PkgValidateErrComponentNameNotUnique, "duplicate"), - fmt.Sprintf(lang.PkgValidateErrGroupOneComponent, "a-group", "required-in-group"), - fmt.Sprintf(lang.PkgValidateErrGroupMultipleDefaults, "multi-default", "multi-default", "multi-default-2"), + fmt.Errorf(PkgValidateErrConstant, fmt.Errorf("provided value for constant %s does not match pattern %s", "BAD", "^good_val$")).Error(), + fmt.Sprintf(PkgValidateErrComponentReqDefault, "invalid"), + fmt.Sprintf(PkgValidateErrChartNameNotUnique, "chart1"), + fmt.Sprintf(PkgValidateErrManifestNameNotUnique, "manifest1"), + fmt.Sprintf(PkgValidateErrComponentReqGrouped, "required-in-group"), + fmt.Sprintf(PkgValidateErrComponentNameNotUnique, "duplicate"), + fmt.Sprintf(PkgValidateErrGroupOneComponent, "a-group", "required-in-group"), + fmt.Sprintf(PkgValidateErrGroupMultipleDefaults, "multi-default", "multi-default", "multi-default-2"), }, }, { @@ -123,11 +121,11 @@ func TestZarfPackageValidate(t *testing.T) { }, }, expectedErrs: []string{ - lang.PkgValidateErrInitNoYOLO, - lang.PkgValidateErrYOLONoOCI, - lang.PkgValidateErrYOLONoGit, - lang.PkgValidateErrYOLONoArch, - lang.PkgValidateErrYOLONoDistro, + PkgValidateErrInitNoYOLO, + PkgValidateErrYOLONoOCI, + PkgValidateErrYOLONoGit, + PkgValidateErrYOLONoArch, + PkgValidateErrYOLONoDistro, }, }, } @@ -163,12 +161,12 @@ func TestValidateManifest(t *testing.T) { { name: "long name", manifest: ZarfManifest{Name: longName, Files: []string{"a-file"}}, - expectedErrs: []string{fmt.Sprintf(lang.PkgValidateErrManifestNameLength, longName, ZarfMaxChartNameLength)}, + expectedErrs: []string{fmt.Sprintf(PkgValidateErrManifestNameLength, longName, ZarfMaxChartNameLength)}, }, { name: "no files or kustomize", manifest: ZarfManifest{Name: "nothing-there"}, - expectedErrs: []string{fmt.Sprintf(lang.PkgValidateErrManifestFileOrKustomize, "nothing-there")}, + expectedErrs: []string{fmt.Sprintf(PkgValidateErrManifestFileOrKustomize, "nothing-there")}, }, } for _, tt := range tests { @@ -268,23 +266,23 @@ func TestValidateChart(t *testing.T) { name: "long name", chart: ZarfChart{Name: longName, Namespace: "whatever", URL: "http://whatever", Version: "v1.0.0"}, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrChartName, longName, ZarfMaxChartNameLength), + fmt.Sprintf(PkgValidateErrChartName, longName, ZarfMaxChartNameLength), }, }, { name: "no url, local path, version, or namespace", chart: ZarfChart{Name: "invalid"}, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrChartNamespaceMissing, "invalid"), - fmt.Sprintf(lang.PkgValidateErrChartURLOrPath, "invalid"), - fmt.Sprintf(lang.PkgValidateErrChartVersion, "invalid"), + fmt.Sprintf(PkgValidateErrChartNamespaceMissing, "invalid"), + fmt.Sprintf(PkgValidateErrChartURLOrPath, "invalid"), + fmt.Sprintf(PkgValidateErrChartVersion, "invalid"), }, }, { name: "both url and local path", chart: ZarfChart{Name: "invalid", Namespace: "whatever", URL: "http://whatever", LocalPath: "wherever", Version: "v1.0.0"}, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrChartURLOrPath, "invalid"), + fmt.Sprintf(PkgValidateErrChartURLOrPath, "invalid"), }, }, { @@ -361,7 +359,7 @@ func TestValidateComponentActions(t *testing.T) { Before: []ZarfComponentAction{ { Cmd: "echo 'invalid setVariable'", - SetVariables: []variables.Variable{{Name: "VAR"}}, + SetVariables: []Variable{{Name: "VAR"}}, }, }, }, @@ -403,10 +401,10 @@ func TestValidateComponentActions(t *testing.T) { }, }, expectedErrs: []string{ - fmt.Errorf(lang.PkgValidateErrAction, fmt.Errorf(lang.PkgValidateErrActionCmdWait, "create")).Error(), - fmt.Errorf(lang.PkgValidateErrAction, fmt.Errorf(lang.PkgValidateErrActionCmdWait, "deploy")).Error(), - fmt.Errorf(lang.PkgValidateErrAction, fmt.Errorf(lang.PkgValidateErrActionCmdWait, "remove")).Error(), - fmt.Errorf(lang.PkgValidateErrAction, fmt.Errorf(lang.PkgValidateErrActionCmdWait, "remove2")).Error(), + fmt.Errorf(PkgValidateErrAction, fmt.Errorf(PkgValidateErrActionCmdWait, "create")).Error(), + fmt.Errorf(PkgValidateErrAction, fmt.Errorf(PkgValidateErrActionCmdWait, "deploy")).Error(), + fmt.Errorf(PkgValidateErrAction, fmt.Errorf(PkgValidateErrActionCmdWait, "remove")).Error(), + fmt.Errorf(PkgValidateErrAction, fmt.Errorf(PkgValidateErrActionCmdWait, "remove2")).Error(), }, }, } @@ -444,8 +442,8 @@ func TestValidateComponentAction(t *testing.T) { Wait: &ZarfComponentActionWait{}, }, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrActionCmdWait, "ls"), - lang.PkgValidateErrActionClusterNetwork, + fmt.Sprintf(PkgValidateErrActionCmdWait, "ls"), + PkgValidateErrActionClusterNetwork, }, }, { @@ -453,7 +451,8 @@ func TestValidateComponentAction(t *testing.T) { action: ZarfComponentAction{ Wait: &ZarfComponentActionWait{Cluster: &ZarfComponentActionWaitCluster{}, Network: &ZarfComponentActionWaitNetwork{}}, }, - expectedErrs: []string{fmt.Sprintf(lang.PkgValidateErrActionClusterNetwork)}, + //nolint:staticcheck //ignore + expectedErrs: []string{fmt.Sprintf(PkgValidateErrActionClusterNetwork)}, }, } @@ -507,7 +506,7 @@ func TestValidateZarfComponent(t *testing.T) { Name: "neither", }, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrImportDefinition, "neither", "neither a path nor a URL was provided"), + fmt.Sprintf(PkgValidateErrImportDefinition, "neither", "neither a path nor a URL was provided"), }, }, { @@ -520,7 +519,7 @@ func TestValidateZarfComponent(t *testing.T) { }, }, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrImportDefinition, "both", "both a path and a URL were provided"), + fmt.Sprintf(PkgValidateErrImportDefinition, "both", "both a path and a URL were provided"), }, }, { @@ -532,7 +531,7 @@ func TestValidateZarfComponent(t *testing.T) { }, }, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrImportDefinition, "abs-path", "path cannot be an absolute path"), + fmt.Sprintf(PkgValidateErrImportDefinition, "abs-path", "path cannot be an absolute path"), }, }, { @@ -544,7 +543,7 @@ func TestValidateZarfComponent(t *testing.T) { }, }, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrImportDefinition, "bad-url", "URL is not a valid OCI URL"), + fmt.Sprintf(PkgValidateErrImportDefinition, "bad-url", "URL is not a valid OCI URL"), }, }, } diff --git a/src/api/v1beta1/component.go b/src/api/v1beta1/component.go index 593c8c1abb..2d27777c54 100644 --- a/src/api/v1beta1/component.go +++ b/src/api/v1beta1/component.go @@ -6,8 +6,6 @@ package v1beta1 import ( "github.com/invopop/jsonschema" - "github.com/zarf-dev/zarf/src/pkg/utils/exec" - "github.com/zarf-dev/zarf/src/pkg/variables" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -136,28 +134,32 @@ type ZarfChart struct { Variables []ZarfChartVariable `json:"variables,omitempty"` } +// HelmRepoSource represents a Helm chart stored in a Helm repository. type HelmRepoSource struct { // The name of a chart within a Helm repository (defaults to the Zarf name of the chart). RepoName string `json:"repoName,omitempty"` // The URL of the chart repository where the helm chart is stored. - Url string `json:"url"` + URL string `json:"url"` } +// GitRepoSource represents a Helm chart stored in a Git repository. type GitRepoSource struct { // The URL of the git repository where the helm chart is stored. - Url string `json:"url"` + URL string `json:"url"` // The sub directory to the chart within a git repo. Path string `json:"path,omitempty"` } +// LocalRepoSource represents a Helm chart stored locally. type LocalRepoSource struct { // The path to a local chart's folder or .tgz archive. Path string `json:"path,omitempty"` } +// OCISource represents a Helm chart stored in an OCI registry. type OCISource struct { // The URL of the OCI registry where the helm chart is stored. - Url string `json:"url"` + URL string `json:"url"` } // ZarfChartVariable represents a variable that can be set for a Helm chart overrides. @@ -223,7 +225,7 @@ type ZarfComponentActionDefaults struct { // Additional environment variables for commands. Env []string `json:"env,omitempty"` // (cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems. - Shell exec.Shell `json:"shell,omitempty"` + Shell Shell `json:"shell,omitempty"` } // ZarfComponentAction represents a single action to run during a zarf package operation. @@ -241,9 +243,9 @@ type ZarfComponentAction struct { // The command to run. Must specify either cmd or wait for the action to do anything. Cmd string `json:"cmd,omitempty"` // (cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems. - Shell *exec.Shell `json:"shell,omitempty"` + Shell *Shell `json:"shell,omitempty"` // (onDeploy/cmd only) An array of variables to update with the output of the command. These variables will be available to all remaining actions and components in the package. - SetVariables []variables.Variable `json:"setVariables,omitempty"` + SetVariables []Variable `json:"setVariables,omitempty"` // Description of the action to be displayed during package execution instead of the command. Description string `json:"description,omitempty"` // Wait for a condition to be met before continuing. Must specify either cmd or wait for the action. See the 'zarf tools wait-for' command for more info. @@ -324,3 +326,10 @@ func (ZarfComponentImport) JSONSchemaExtend(schema *jsonschema.Schema) { path.Not = notSchema url.Not = notSchema } + +// Shell represents the desired shell to use for a given command +type Shell struct { + Windows string `json:"windows,omitempty" jsonschema:"description=(default 'powershell') Indicates a preference for the shell to use on Windows systems (note that choosing 'cmd' will turn off migrations like touch -> New-Item),example=powershell,example=cmd,example=pwsh,example=sh,example=bash,example=gsh"` + Linux string `json:"linux,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on Linux systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"` + Darwin string `json:"darwin,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on macOS systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"` +} diff --git a/src/api/v1beta1/package.go b/src/api/v1beta1/package.go index cf37f59813..e190102e54 100644 --- a/src/api/v1beta1/package.go +++ b/src/api/v1beta1/package.go @@ -5,7 +5,18 @@ package v1beta1 import ( - "github.com/zarf-dev/zarf/src/pkg/variables" + "fmt" + "regexp" +) + +// VariableType represents a type of a Zarf package variable +type VariableType string + +const ( + // RawVariableType is the default type for a Zarf package variable + RawVariableType VariableType = "raw" + // FileVariableType is a type for a Zarf package variable that loads its contents from a file + FileVariableType VariableType = "file" ) // ZarfPackageKind is an enum of the different kinds of Zarf packages. @@ -16,9 +27,11 @@ const ( ZarfInitConfig ZarfPackageKind = "ZarfInitConfig" // ZarfPackageConfig is the default kind of Zarf package, primarily used during `zarf package`. ZarfPackageConfig ZarfPackageKind = "ZarfPackageConfig" - APIVersion string = "zarf.dev/v1beta1" + // APIVersion the api version of this package. + APIVersion string = "zarf.dev/v1beta1" ) +// ZarfPackageTemplatePrefix is the prefix for package templates. const ( ZarfPackageTemplatePrefix = "###ZARF_PKG_TMPL_" ) @@ -36,9 +49,9 @@ type ZarfPackage struct { // List of components to deploy in this package. Components []ZarfComponent `json:"components" jsonschema:"minItems=1"` // Constant template values applied on deploy for K8s resources. - Constants []variables.Constant `json:"constants,omitempty"` + Constants []Constant `json:"constants,omitempty"` // Variable template values applied on deploy for K8s resources. - Variables []variables.InteractiveVariable `json:"variables,omitempty"` + Variables []InteractiveVariable `json:"variables,omitempty"` } // IsInitConfig returns whether a Zarf package is an init config. @@ -66,6 +79,60 @@ func (pkg ZarfPackage) IsSBOMAble() bool { return false } +// Variable represents a variable that has a value set programmatically +type Variable struct { + // The name to be used for the variable + Name string `json:"name" jsonschema:"pattern=^[A-Z0-9_]+$"` + // Whether to mark this variable as sensitive to not print it in the log + Sensitive bool `json:"sensitive,omitempty"` + // Whether to automatically indent the variable's value (if multiline) when templating. Based on the number of chars before the start of ###ZARF_VAR_. + AutoIndent bool `json:"autoIndent,omitempty"` + // An optional regex pattern that a variable value must match before a package deployment can continue. + Pattern string `json:"pattern,omitempty"` + // Changes the handling of a variable to load contents differently (i.e. from a file rather than as a raw variable - templated files should be kept below 1 MiB) + Type VariableType `json:"type,omitempty" jsonschema:"enum=raw,enum=file"` +} + +// InteractiveVariable is a variable that can be used to prompt a user for more information +type InteractiveVariable struct { + Variable `json:",inline"` + // A description of the variable to be used when prompting the user a value + Description string `json:"description,omitempty"` + // The default value to use for the variable + Default string `json:"default,omitempty"` + // Whether to prompt the user for input for this variable + Prompt bool `json:"prompt,omitempty"` +} + +// Constant are constants that can be used to dynamically template K8s resources or run in actions. +type Constant struct { + // The name to be used for the constant + Name string `json:"name" jsonschema:"pattern=^[A-Z0-9_]+$"` + // The value to set for the constant during deploy + Value string `json:"value"` + // A description of the constant to explain its purpose on package create or deploy confirmation prompts + Description string `json:"description,omitempty"` + // Whether to automatically indent the variable's value (if multiline) when templating. Based on the number of chars before the start of ###ZARF_CONST_. + AutoIndent bool `json:"autoIndent,omitempty"` + // An optional regex pattern that a constant value must match before a package can be created. + Pattern string `json:"pattern,omitempty"` +} + +// SetVariable tracks internal variables that have been set during this run of Zarf +type SetVariable struct { + Variable `json:",inline"` + // The value the variable is currently set with + Value string `json:"value"` +} + +// Validate runs all validation checks on a package constant. +func (c Constant) Validate() error { + if !regexp.MustCompile(c.Pattern).MatchString(c.Value) { + return fmt.Errorf("provided value for constant %s does not match pattern %s", c.Name, c.Pattern) + } + return nil +} + // ZarfMetadata lists information about the current ZarfPackage. type ZarfMetadata struct { // Name to identify this Zarf package. diff --git a/src/api/v1beta1/translate.go b/src/api/v1beta1/translate.go index 0c9571e277..78d2182492 100644 --- a/src/api/v1beta1/translate.go +++ b/src/api/v1beta1/translate.go @@ -15,6 +15,7 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// TranslateAlphaPackage translates a v1alpha1.ZarfPackage to a v1beta1.ZarfPackage func TranslateAlphaPackage(alphaPkg v1alpha1.ZarfPackage) (ZarfPackage, error) { var betaPkg ZarfPackage @@ -63,21 +64,21 @@ func TranslateAlphaPackage(alphaPkg v1alpha1.ZarfPackage) (ZarfPackage, error) { for i := range betaPkg.Components { betaPkg.Components[i].Optional = helpers.BoolPtr(!alphaPkg.Components[i].IsRequired()) for j := range betaPkg.Components[i].Charts { - oldUrl := alphaPkg.Components[i].Charts[j].URL - if helpers.IsOCIURL(oldUrl) { - betaPkg.Components[i].Charts[j].OCI.Url = oldUrl - } else if strings.HasSuffix(oldUrl, ".git") { - betaPkg.Components[i].Charts[j].Git.Url = oldUrl + oldURL := alphaPkg.Components[i].Charts[j].URL + if helpers.IsOCIURL(oldURL) { + betaPkg.Components[i].Charts[j].OCI.URL = oldURL + } else if strings.HasSuffix(oldURL, ".git") { + betaPkg.Components[i].Charts[j].Git.URL = oldURL betaPkg.Components[i].Charts[j].Git.Path = alphaPkg.Components[i].Charts[j].GitPath } else { - betaPkg.Components[i].Charts[j].Helm.Url = oldUrl + betaPkg.Components[i].Charts[j].Helm.URL = oldURL betaPkg.Components[i].Charts[j].Helm.RepoName = alphaPkg.Components[i].Charts[j].RepoName } betaPkg.Components[i].Charts[j].Local.Path = alphaPkg.Components[i].Charts[j].LocalPath betaPkg.Components[i].Charts[j].Wait = helpers.BoolPtr(!alphaPkg.Components[i].Charts[j].NoWait) } - for j := range betaPkg.Components[i].Manifests{ + for j := range betaPkg.Components[i].Manifests { betaPkg.Components[i].Manifests[j].Wait = helpers.BoolPtr(!alphaPkg.Components[i].Manifests[j].NoWait) } betaPkg.Components[i].Actions.OnCreate = transformActionSet(betaPkg.Components[i].Actions.OnCreate, alphaPkg.Components[i].Actions.OnCreate) diff --git a/src/api/v1beta1/translate_test.go b/src/api/v1beta1/translate_test.go index 67f0592ab3..af95e10d47 100644 --- a/src/api/v1beta1/translate_test.go +++ b/src/api/v1beta1/translate_test.go @@ -40,7 +40,7 @@ func TestTranslate(t *testing.T) { Required: helpers.BoolPtr(true), }, { - Name: "manifests", + Name: "manifests", Manifests: []v1alpha1.ZarfManifest{ { NoWait: true, @@ -210,21 +210,21 @@ func TestTranslate(t *testing.T) { { Wait: helpers.BoolPtr(false), Helm: HelmRepoSource{ - Url: "https://example.com/chart", + URL: "https://example.com/chart", RepoName: "repo1", }, }, { Wait: helpers.BoolPtr(true), Git: GitRepoSource{ - Url: "https://example.com/chart.git", + URL: "https://example.com/chart.git", Path: "path/to/chart2", }, }, { Wait: helpers.BoolPtr(true), OCI: OCISource{ - Url: "oci://example.com/chart", + URL: "oci://example.com/chart", }, }, { diff --git a/src/config/lang/english.go b/src/config/lang/english.go index 8e821b03f2..1bca5f773a 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -30,7 +30,8 @@ const ( // Lint messages const ( - UnsetVarLintWarning = "There are templates that are not set and won't be evaluated during lint" + UnsetVarLintWarning = "There are templates that are not set and won't be evaluated during lint" + PkgValidateTemplateDeprecation = "Package template %q is using the deprecated syntax ###ZARF_PKG_VAR_%s###. This will be removed in Zarf v1.0.0. Please update to ###ZARF_PKG_TMPL_%s###." ) // Zarf CLI commands. @@ -619,38 +620,6 @@ const ( PkgCreateErrDifferentialNoVersion = "unable to create differential package. Please ensure both package versions are set" ) -// Package validate -const ( - PkgValidateTemplateDeprecation = "Package template %q is using the deprecated syntax ###ZARF_PKG_VAR_%s###. This will be removed in Zarf v1.0.0. Please update to ###ZARF_PKG_TMPL_%s###." - PkgValidateErrAction = "invalid action: %w" - PkgValidateErrActionCmdWait = "action %q cannot be both a command and wait action" - PkgValidateErrActionClusterNetwork = "a single wait action must contain only one of cluster or network" - PkgValidateErrChart = "invalid chart definition: %w" - PkgValidateErrChartName = "chart %q exceed the maximum length of %d characters" - PkgValidateErrChartNameNotUnique = "chart name %q is not unique" - PkgValidateErrChartNamespaceMissing = "chart %q must include a namespace" - PkgValidateErrChartURLOrPath = "chart %q must have either a url or localPath" - PkgValidateErrChartVersion = "chart %q must include a chart version" - PkgValidateErrComponentNameNotUnique = "component name %q is not unique" - PkgValidateErrComponentReqDefault = "component %q cannot be both required and default" - PkgValidateErrComponentReqGrouped = "component %q cannot be both required and grouped" - PkgValidateErrGroupMultipleDefaults = "group %q has multiple defaults (%q, %q)" - PkgValidateErrGroupOneComponent = "group %q only has one component (%q)" - PkgValidateErrConstant = "invalid package constant: %w" - PkgValidateErrImportDefinition = "invalid imported definition for %s: %s" - PkgValidateErrInitNoYOLO = "sorry, you can't YOLO an init package" - PkgValidateErrManifest = "invalid manifest definition: %w" - PkgValidateErrManifestFileOrKustomize = "manifest %q must have at least one file or kustomization" - PkgValidateErrManifestNameLength = "manifest %q exceed the maximum length of %d characters" - PkgValidateErrManifestNameNotUnique = "manifest name %q is not unique" - PkgValidateErrPkgConstantPattern = "provided value for constant %q does not match pattern %q" - PkgValidateErrVariable = "invalid package variable: %w" - PkgValidateErrYOLONoArch = "cluster architecture not allowed in YOLO" - PkgValidateErrYOLONoDistro = "cluster distros not allowed in YOLO" - PkgValidateErrYOLONoGit = "git repos not allowed in YOLO" - PkgValidateErrYOLONoOCI = "OCI images not allowed in YOLO" -) - // Collection of reusable error messages. var ( ErrInitNotFound = errors.New("this command requires a zarf-init package, but one was not found on the local system. Re-run the last command again without '--confirm' to download the package") diff --git a/src/internal/packager/helm/zarf.go b/src/internal/packager/helm/zarf.go index 9f72dce5a4..03e4db2ed0 100644 --- a/src/internal/packager/helm/zarf.go +++ b/src/internal/packager/helm/zarf.go @@ -23,7 +23,6 @@ import ( "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/transform" "github.com/zarf-dev/zarf/src/pkg/utils" - "github.com/zarf-dev/zarf/src/pkg/variables" ) // UpdateZarfRegistryValues updates the Zarf registry deployment with the new state values @@ -103,7 +102,7 @@ func (h *Helm) UpdateZarfAgentValues(ctx context.Context) error { Namespace: "zarf", ReleaseName: release.Name, } - h.variableConfig.SetConstants([]variables.Constant{ + h.variableConfig.SetConstants([]v1alpha1.Constant{ { Name: "AGENT_IMAGE", Value: agentImage.Path, diff --git a/src/internal/packager/template/template.go b/src/internal/packager/template/template.go index 645982865e..872754ce4d 100644 --- a/src/internal/packager/template/template.go +++ b/src/internal/packager/template/template.go @@ -10,6 +10,7 @@ import ( "log/slog" "strings" + "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/types" "github.com/defenseunicorns/pkg/helpers/v2" @@ -26,7 +27,7 @@ const ( // GetZarfVariableConfig gets a variable configuration specific to Zarf func GetZarfVariableConfig() *variables.VariableConfig { - prompt := func(variable variables.InteractiveVariable) (value string, err error) { + prompt := func(variable v1alpha1.InteractiveVariable) (value string, err error) { if config.CommonOptions.Confirm { return variable.Default, nil } diff --git a/src/pkg/cluster/data.go b/src/pkg/cluster/data.go index 8f1687123a..7c57780a26 100644 --- a/src/pkg/cluster/data.go +++ b/src/pkg/cluster/data.go @@ -53,7 +53,7 @@ func (c *Cluster) HandleDataInjection(ctx context.Context, data v1alpha1.ZarfDat } // Get the OS shell to execute commands in - shell, shellArgs := exec.GetOSShell(exec.Shell{Windows: "cmd"}) + shell, shellArgs := exec.GetOSShell(v1alpha1.Shell{Windows: "cmd"}) if _, _, err := exec.Cmd(shell, append(shellArgs, "tar --version")...); err != nil { return fmt.Errorf("unable to execute tar, ensure it is installed in the $PATH: %w", err) diff --git a/src/pkg/interactive/prompt.go b/src/pkg/interactive/prompt.go index c22b05e449..5af5f9c451 100644 --- a/src/pkg/interactive/prompt.go +++ b/src/pkg/interactive/prompt.go @@ -8,8 +8,8 @@ import ( "fmt" "github.com/AlecAivazis/survey/v2" + "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/pkg/message" - "github.com/zarf-dev/zarf/src/pkg/variables" ) // PromptSigPassword prompts the user for the password to their private key @@ -23,7 +23,7 @@ func PromptSigPassword() ([]byte, error) { } // PromptVariable prompts the user for a value for a variable -func PromptVariable(variable variables.InteractiveVariable) (value string, err error) { +func PromptVariable(variable v1alpha1.InteractiveVariable) (value string, err error) { if variable.Description != "" { message.Question(variable.Description) } diff --git a/src/pkg/lint/schema_test.go b/src/pkg/lint/schema_test.go index ed6a7dc5a0..bd8b576a83 100644 --- a/src/pkg/lint/schema_test.go +++ b/src/pkg/lint/schema_test.go @@ -12,7 +12,6 @@ import ( goyaml "github.com/goccy/go-yaml" "github.com/stretchr/testify/require" "github.com/zarf-dev/zarf/src/api/v1alpha1" - "github.com/zarf-dev/zarf/src/pkg/variables" ) func TestZarfSchema(t *testing.T) { @@ -80,7 +79,7 @@ func TestZarfSchema(t *testing.T) { Before: []v1alpha1.ZarfComponentAction{ { Cmd: "echo 'invalid setVariable'", - SetVariables: []variables.Variable{{Name: "not_uppercase"}}, + SetVariables: []v1alpha1.Variable{{Name: "not_uppercase"}}, }, }, }, @@ -88,19 +87,19 @@ func TestZarfSchema(t *testing.T) { OnSuccess: []v1alpha1.ZarfComponentAction{ { Cmd: "echo 'invalid setVariable'", - SetVariables: []variables.Variable{{Name: "not_uppercase"}}, + SetVariables: []v1alpha1.Variable{{Name: "not_uppercase"}}, }, }, }, }, }, }, - Variables: []variables.InteractiveVariable{ + Variables: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "not_uppercase"}, + Variable: v1alpha1.Variable{Name: "not_uppercase"}, }, }, - Constants: []variables.Constant{ + Constants: []v1alpha1.Constant{ { Name: "not_uppercase", }, diff --git a/src/pkg/packager/actions/actions.go b/src/pkg/packager/actions/actions.go index 4fdf71fdd5..983f2f6bde 100644 --- a/src/pkg/packager/actions/actions.go +++ b/src/pkg/packager/actions/actions.go @@ -70,7 +70,7 @@ func runAction(ctx context.Context, defaultCfg v1alpha1.ZarfComponentActionDefau d := "" action.Dir = &d action.Env = []string{} - action.SetVariables = []variables.Variable{} + action.SetVariables = []v1alpha1.Variable{} } if action.Description != "" { @@ -204,7 +204,7 @@ func convertWaitToCmd(_ context.Context, wait v1alpha1.ZarfComponentActionWait, } // Perform some basic string mutations to make commands more useful. -func actionCmdMutation(_ context.Context, cmd string, shellPref exec.Shell) (string, error) { +func actionCmdMutation(_ context.Context, cmd string, shellPref v1alpha1.Shell) (string, error) { zarfCommand, err := utils.GetFinalExecutableCommand() if err != nil { return cmd, err @@ -274,7 +274,7 @@ func actionGetCfg(_ context.Context, cfg v1alpha1.ZarfComponentActionDefaults, a return cfg } -func actionRun(ctx context.Context, cfg v1alpha1.ZarfComponentActionDefaults, cmd string, shellPref exec.Shell, spinner *message.Spinner) (string, error) { +func actionRun(ctx context.Context, cfg v1alpha1.ZarfComponentActionDefaults, cmd string, shellPref v1alpha1.Shell, spinner *message.Spinner) (string, error) { shell, shellArgs := exec.GetOSShell(shellPref) message.Debugf("Running command in %s: %s", shell, cmd) diff --git a/src/pkg/packager/composer/list.go b/src/pkg/packager/composer/list.go index 8d5531b85e..9d2bce9d97 100644 --- a/src/pkg/packager/composer/list.go +++ b/src/pkg/packager/composer/list.go @@ -16,7 +16,6 @@ import ( "github.com/zarf-dev/zarf/src/pkg/layout" "github.com/zarf-dev/zarf/src/pkg/packager/deprecated" "github.com/zarf-dev/zarf/src/pkg/utils" - "github.com/zarf-dev/zarf/src/pkg/variables" "github.com/zarf-dev/zarf/src/pkg/zoci" ) @@ -26,8 +25,8 @@ type Node struct { index int - vars []variables.InteractiveVariable - consts []variables.Constant + vars []v1alpha1.InteractiveVariable + consts []v1alpha1.Constant relativeToHead string originalPackageName string @@ -96,7 +95,7 @@ func (ic *ImportChain) Tail() *Node { } func (ic *ImportChain) append(c v1alpha1.ZarfComponent, index int, originalPackageName string, - relativeToHead string, vars []variables.InteractiveVariable, consts []variables.Constant) { + relativeToHead string, vars []v1alpha1.InteractiveVariable, consts []v1alpha1.Constant) { node := &Node{ ZarfComponent: c, index: index, @@ -313,8 +312,8 @@ func (ic *ImportChain) Compose(ctx context.Context) (composed *v1alpha1.ZarfComp } // MergeVariables merges variables from the import chain -func (ic *ImportChain) MergeVariables(existing []variables.InteractiveVariable) (merged []variables.InteractiveVariable) { - exists := func(v1 variables.InteractiveVariable, v2 variables.InteractiveVariable) bool { +func (ic *ImportChain) MergeVariables(existing []v1alpha1.InteractiveVariable) (merged []v1alpha1.InteractiveVariable) { + exists := func(v1 v1alpha1.InteractiveVariable, v2 v1alpha1.InteractiveVariable) bool { return v1.Name == v2.Name } @@ -330,8 +329,8 @@ func (ic *ImportChain) MergeVariables(existing []variables.InteractiveVariable) } // MergeConstants merges constants from the import chain -func (ic *ImportChain) MergeConstants(existing []variables.Constant) (merged []variables.Constant) { - exists := func(c1 variables.Constant, c2 variables.Constant) bool { +func (ic *ImportChain) MergeConstants(existing []v1alpha1.Constant) (merged []v1alpha1.Constant) { + exists := func(c1 v1alpha1.Constant, c2 v1alpha1.Constant) bool { return c1.Name == c2.Name } diff --git a/src/pkg/packager/composer/list_test.go b/src/pkg/packager/composer/list_test.go index 56e266738b..beff63b524 100644 --- a/src/pkg/packager/composer/list_test.go +++ b/src/pkg/packager/composer/list_test.go @@ -14,7 +14,6 @@ import ( "github.com/stretchr/testify/require" "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/api/v1alpha1/extensions" - "github.com/zarf-dev/zarf/src/pkg/variables" ) func TestNewImportChain(t *testing.T) { @@ -251,16 +250,16 @@ func TestMerging(t *testing.T) { t.Parallel() head := Node{ - vars: []variables.InteractiveVariable{ + vars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, Default: "head", }, { - Variable: variables.Variable{Name: "HEAD"}, + Variable: v1alpha1.Variable{Name: "HEAD"}, }, }, - consts: []variables.Constant{ + consts: []v1alpha1.Constant{ { Name: "TEST", Value: "head", @@ -271,16 +270,16 @@ func TestMerging(t *testing.T) { }, } tail := Node{ - vars: []variables.InteractiveVariable{ + vars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, Default: "tail", }, { - Variable: variables.Variable{Name: "TAIL"}, + Variable: v1alpha1.Variable{Name: "TAIL"}, }, }, - consts: []variables.Constant{ + consts: []v1alpha1.Constant{ { Name: "TEST", Value: "tail", @@ -297,30 +296,30 @@ func TestMerging(t *testing.T) { tests := []struct { name string ic *ImportChain - existingVars []variables.InteractiveVariable - existingConsts []variables.Constant - expectedVars []variables.InteractiveVariable - expectedConsts []variables.Constant + existingVars []v1alpha1.InteractiveVariable + existingConsts []v1alpha1.Constant + expectedVars []v1alpha1.InteractiveVariable + expectedConsts []v1alpha1.Constant }{ { name: "empty-ic", ic: &ImportChain{}, - existingVars: []variables.InteractiveVariable{ + existingVars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, }, }, - existingConsts: []variables.Constant{ + existingConsts: []v1alpha1.Constant{ { Name: "TEST", }, }, - expectedVars: []variables.InteractiveVariable{ + expectedVars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, }, }, - expectedConsts: []variables.Constant{ + expectedConsts: []v1alpha1.Constant{ { Name: "TEST", }, @@ -329,21 +328,21 @@ func TestMerging(t *testing.T) { { name: "no-existing", ic: testIC, - existingVars: []variables.InteractiveVariable{}, - existingConsts: []variables.Constant{}, - expectedVars: []variables.InteractiveVariable{ + existingVars: []v1alpha1.InteractiveVariable{}, + existingConsts: []v1alpha1.Constant{}, + expectedVars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, Default: "head", }, { - Variable: variables.Variable{Name: "HEAD"}, + Variable: v1alpha1.Variable{Name: "HEAD"}, }, { - Variable: variables.Variable{Name: "TAIL"}, + Variable: v1alpha1.Variable{Name: "TAIL"}, }, }, - expectedConsts: []variables.Constant{ + expectedConsts: []v1alpha1.Constant{ { Name: "TEST", Value: "head", @@ -359,16 +358,16 @@ func TestMerging(t *testing.T) { { name: "with-existing", ic: testIC, - existingVars: []variables.InteractiveVariable{ + existingVars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, Default: "existing", }, { - Variable: variables.Variable{Name: "EXISTING"}, + Variable: v1alpha1.Variable{Name: "EXISTING"}, }, }, - existingConsts: []variables.Constant{ + existingConsts: []v1alpha1.Constant{ { Name: "TEST", Value: "existing", @@ -377,22 +376,22 @@ func TestMerging(t *testing.T) { Name: "EXISTING", }, }, - expectedVars: []variables.InteractiveVariable{ + expectedVars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, Default: "existing", }, { - Variable: variables.Variable{Name: "EXISTING"}, + Variable: v1alpha1.Variable{Name: "EXISTING"}, }, { - Variable: variables.Variable{Name: "HEAD"}, + Variable: v1alpha1.Variable{Name: "HEAD"}, }, { - Variable: variables.Variable{Name: "TAIL"}, + Variable: v1alpha1.Variable{Name: "TAIL"}, }, }, - expectedConsts: []variables.Constant{ + expectedConsts: []v1alpha1.Constant{ { Name: "TEST", Value: "existing", diff --git a/src/pkg/packager/creator/template.go b/src/pkg/packager/creator/template.go index 65d0d56c99..1c5037302f 100644 --- a/src/pkg/packager/creator/template.go +++ b/src/pkg/packager/creator/template.go @@ -12,7 +12,6 @@ import ( "github.com/zarf-dev/zarf/src/config/lang" "github.com/zarf-dev/zarf/src/pkg/interactive" "github.com/zarf-dev/zarf/src/pkg/utils" - "github.com/zarf-dev/zarf/src/pkg/variables" ) // FillActiveTemplate merges user-specified variables into the configuration templates of a zarf.yaml. @@ -33,8 +32,8 @@ func FillActiveTemplate(pkg v1alpha1.ZarfPackage, setVariables map[string]string _, present := setVariables[key] if !present && !config.CommonOptions.Confirm { - setVal, err := interactive.PromptVariable(variables.InteractiveVariable{ - Variable: variables.Variable{Name: key}, + setVal, err := interactive.PromptVariable(v1alpha1.InteractiveVariable{ + Variable: v1alpha1.Variable{Name: key}, }) if err != nil { return err diff --git a/src/pkg/packager/deploy_test.go b/src/pkg/packager/deploy_test.go index d0802a4ac5..a1f32aa346 100644 --- a/src/pkg/packager/deploy_test.go +++ b/src/pkg/packager/deploy_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/require" "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/pkg/packager/sources" - "github.com/zarf-dev/zarf/src/pkg/variables" "github.com/zarf-dev/zarf/src/types" ) @@ -216,7 +215,7 @@ func TestGenerateValuesOverrides(t *testing.T) { p, err := New(&types.PackagerConfig{DeployOpts: tt.deployOpts}, WithSource(&sources.TarballSource{})) require.NoError(t, err) for k, v := range tt.setVariables { - p.variableConfig.SetVariable(k, v, false, false, variables.RawVariableType) + p.variableConfig.SetVariable(k, v, false, false, v1alpha1.RawVariableType) } got, err := p.generateValuesOverrides(tt.chart, tt.componentName) diff --git a/src/pkg/packager/deprecated/pluralize-set-variable.go b/src/pkg/packager/deprecated/pluralize-set-variable.go index 04981f2790..2890038336 100644 --- a/src/pkg/packager/deprecated/pluralize-set-variable.go +++ b/src/pkg/packager/deprecated/pluralize-set-variable.go @@ -8,7 +8,6 @@ import ( "fmt" "github.com/zarf-dev/zarf/src/api/v1alpha1" - "github.com/zarf-dev/zarf/src/pkg/variables" ) func migrateSetVariableToSetVariables(c v1alpha1.ZarfComponent) (v1alpha1.ZarfComponent, string) { @@ -18,7 +17,7 @@ func migrateSetVariableToSetVariables(c v1alpha1.ZarfComponent) (v1alpha1.ZarfCo for i := range actions { if actions[i].DeprecatedSetVariable != "" && len(actions[i].SetVariables) < 1 { hasSetVariable = true - actions[i].SetVariables = []variables.Variable{ + actions[i].SetVariables = []v1alpha1.Variable{ { Name: actions[i].DeprecatedSetVariable, Sensitive: false, diff --git a/src/pkg/utils/exec/exec.go b/src/pkg/utils/exec/exec.go index 4ac90b6a92..3f76e3f23c 100644 --- a/src/pkg/utils/exec/exec.go +++ b/src/pkg/utils/exec/exec.go @@ -16,6 +16,8 @@ import ( "strings" "sync" "testing" + + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) // Config is a struct for configuring the Cmd function. @@ -28,13 +30,6 @@ type Config struct { Stderr io.Writer } -// Shell represents the desired shell to use for a given command -type Shell struct { - Windows string `json:"windows,omitempty" jsonschema:"description=(default 'powershell') Indicates a preference for the shell to use on Windows systems (note that choosing 'cmd' will turn off migrations like touch -> New-Item),example=powershell,example=cmd,example=pwsh,example=sh,example=bash,example=gsh"` - Linux string `json:"linux,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on Linux systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"` - Darwin string `json:"darwin,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on macOS systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"` -} - // PrintCfg is a helper function for returning a Config struct with Print set to true. func PrintCfg() Config { return Config{Print: true} @@ -161,7 +156,7 @@ func LaunchURL(url string) error { } // GetOSShell returns the shell and shellArgs based on the current OS -func GetOSShell(shellPref Shell) (string, []string) { +func GetOSShell(shellPref v1alpha1.Shell) (string, []string) { var shell string var shellArgs []string powershellShellArgs := []string{"-Command", "$ErrorActionPreference = 'Stop';"} diff --git a/src/pkg/utils/wait.go b/src/pkg/utils/wait.go index a6727d9e35..dbcbbf0267 100644 --- a/src/pkg/utils/wait.go +++ b/src/pkg/utils/wait.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/pkg/utils/exec" "github.com/zarf-dev/zarf/src/pkg/message" @@ -77,7 +78,7 @@ func ExecuteWait(waitTimeout, waitNamespace, condition, kind, identifier string, spinner := message.NewProgressSpinner(existMsg) // Get the OS shell to execute commands in - shell, shellArgs := exec.GetOSShell(exec.Shell{Windows: "cmd"}) + shell, shellArgs := exec.GetOSShell(v1alpha1.Shell{Windows: "cmd"}) defer spinner.Stop() diff --git a/src/pkg/variables/common.go b/src/pkg/variables/common.go index ae2bb5fac5..9c66662901 100644 --- a/src/pkg/variables/common.go +++ b/src/pkg/variables/common.go @@ -6,6 +6,8 @@ package variables import ( "log/slog" + + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) // VariableConfig represents a value to be templated into a text file. @@ -14,14 +16,14 @@ type VariableConfig struct { applicationTemplates map[string]*TextTemplate setVariableMap SetVariableMap - constants []Constant + constants []v1alpha1.Constant - prompt func(variable InteractiveVariable) (value string, err error) + prompt func(variable v1alpha1.InteractiveVariable) (value string, err error) logger *slog.Logger } // New creates a new VariableConfig -func New(templatePrefix string, prompt func(variable InteractiveVariable) (value string, err error), logger *slog.Logger) *VariableConfig { +func New(templatePrefix string, prompt func(variable v1alpha1.InteractiveVariable) (value string, err error), logger *slog.Logger) *VariableConfig { return &VariableConfig{ templatePrefix: templatePrefix, applicationTemplates: make(map[string]*TextTemplate), @@ -37,6 +39,6 @@ func (vc *VariableConfig) SetApplicationTemplates(applicationTemplates map[strin } // SetConstants sets the constants for a variable config (templated as PREFIX_CONST_NAME) -func (vc *VariableConfig) SetConstants(constants []Constant) { +func (vc *VariableConfig) SetConstants(constants []v1alpha1.Constant) { vc.constants = constants } diff --git a/src/pkg/variables/templates.go b/src/pkg/variables/templates.go index e350d77c28..f0153d0baa 100644 --- a/src/pkg/variables/templates.go +++ b/src/pkg/variables/templates.go @@ -12,13 +12,14 @@ import ( "strings" "github.com/defenseunicorns/pkg/helpers/v2" + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) // TextTemplate represents a value to be templated into a text file. type TextTemplate struct { Sensitive bool AutoIndent bool - Type VariableType + Type v1alpha1.VariableType Value string } @@ -97,7 +98,7 @@ func (vc *VariableConfig) ReplaceTextTemplate(path string) error { value = template.Value // Check if the value is a file type and load the value contents from the file - if template.Type == FileVariableType && value != "" { + if template.Type == v1alpha1.FileVariableType && value != "" { if isText, err := helpers.IsTextFile(value); err != nil || !isText { nonTextWarning := fmt.Sprintf("Refusing to load a non-text file for templating %s", templateKey) vc.logger.Warn(nonTextWarning) diff --git a/src/pkg/variables/templates_test.go b/src/pkg/variables/templates_test.go index 8becae631a..73c2ddd7e4 100644 --- a/src/pkg/variables/templates_test.go +++ b/src/pkg/variables/templates_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) var start = ` @@ -79,7 +80,7 @@ func TestReplaceTextTemplate(t *testing.T) { setVariableMap: SetVariableMap{ "REPLACE_ME": {Value: "VAR_REPLACED"}, }, - constants: []Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED"}}, + constants: []v1alpha1.Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED"}}, applicationTemplates: map[string]*TextTemplate{ "###PREFIX_APP_REPLACE_ME###": {Value: "APP_REPLACED"}, }, @@ -92,7 +93,7 @@ func TestReplaceTextTemplate(t *testing.T) { setVariableMap: SetVariableMap{ "REPLACE_ME": {Value: "VAR_REPLACED\nVAR_SECOND"}, }, - constants: []Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED\nCONST_SECOND"}}, + constants: []v1alpha1.Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED\nCONST_SECOND"}}, applicationTemplates: map[string]*TextTemplate{ "###PREFIX_APP_REPLACE_ME###": {Value: "APP_REPLACED\nAPP_SECOND"}, }, @@ -103,9 +104,9 @@ func TestReplaceTextTemplate(t *testing.T) { vc: VariableConfig{ templatePrefix: "PREFIX", setVariableMap: SetVariableMap{ - "REPLACE_ME": {Value: "VAR_REPLACED\nVAR_SECOND", Variable: Variable{AutoIndent: true}}, + "REPLACE_ME": {Value: "VAR_REPLACED\nVAR_SECOND", Variable: v1alpha1.Variable{AutoIndent: true}}, }, - constants: []Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED\nCONST_SECOND", AutoIndent: true}}, + constants: []v1alpha1.Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED\nCONST_SECOND", AutoIndent: true}}, applicationTemplates: map[string]*TextTemplate{ "###PREFIX_APP_REPLACE_ME###": {Value: "APP_REPLACED\nAPP_SECOND", AutoIndent: true}, }, @@ -116,11 +117,11 @@ func TestReplaceTextTemplate(t *testing.T) { vc: VariableConfig{ templatePrefix: "PREFIX", setVariableMap: SetVariableMap{ - "REPLACE_ME": {Value: "testdata/file.txt", Variable: Variable{Type: FileVariableType}}, + "REPLACE_ME": {Value: "testdata/file.txt", Variable: v1alpha1.Variable{Type: v1alpha1.FileVariableType}}, }, - constants: []Constant{{Name: "REPLACE_ME", Value: "CONSTs Don't Support File"}}, + constants: []v1alpha1.Constant{{Name: "REPLACE_ME", Value: "CONSTs Don't Support File"}}, applicationTemplates: map[string]*TextTemplate{ - "###PREFIX_APP_REPLACE_ME###": {Value: "testdata/file.txt", Type: FileVariableType}, + "###PREFIX_APP_REPLACE_ME###": {Value: "testdata/file.txt", Type: v1alpha1.FileVariableType}, }, }, wantContents: file, diff --git a/src/pkg/variables/types.go b/src/pkg/variables/types.go deleted file mode 100644 index 6b3be2863b..0000000000 --- a/src/pkg/variables/types.go +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2021-Present The Zarf Authors - -// Package variables contains functions for interacting with variables -package variables - -import ( - "fmt" - "regexp" - - "github.com/zarf-dev/zarf/src/config/lang" -) - -// VariableType represents a type of a Zarf package variable -type VariableType string - -const ( - // RawVariableType is the default type for a Zarf package variable - RawVariableType VariableType = "raw" - // FileVariableType is a type for a Zarf package variable that loads its contents from a file - FileVariableType VariableType = "file" -) - -var ( - // IsUppercaseNumberUnderscore is a regex for uppercase, numbers and underscores. - // https://regex101.com/r/tfsEuZ/1 - IsUppercaseNumberUnderscore = regexp.MustCompile(`^[A-Z0-9_]+$`).MatchString -) - -// Variable represents a variable that has a value set programmatically -type Variable struct { - // The name to be used for the variable - Name string `json:"name" jsonschema:"pattern=^[A-Z0-9_]+$"` - // Whether to mark this variable as sensitive to not print it in the log - Sensitive bool `json:"sensitive,omitempty"` - // Whether to automatically indent the variable's value (if multiline) when templating. Based on the number of chars before the start of ###ZARF_VAR_. - AutoIndent bool `json:"autoIndent,omitempty"` - // An optional regex pattern that a variable value must match before a package deployment can continue. - Pattern string `json:"pattern,omitempty"` - // Changes the handling of a variable to load contents differently (i.e. from a file rather than as a raw variable - templated files should be kept below 1 MiB) - Type VariableType `json:"type,omitempty" jsonschema:"enum=raw,enum=file"` -} - -// InteractiveVariable is a variable that can be used to prompt a user for more information -type InteractiveVariable struct { - Variable `json:",inline"` - // A description of the variable to be used when prompting the user a value - Description string `json:"description,omitempty"` - // The default value to use for the variable - Default string `json:"default,omitempty"` - // Whether to prompt the user for input for this variable - Prompt bool `json:"prompt,omitempty"` -} - -// Constant are constants that can be used to dynamically template K8s resources or run in actions. -type Constant struct { - // The name to be used for the constant - Name string `json:"name" jsonschema:"pattern=^[A-Z0-9_]+$"` - // The value to set for the constant during deploy - Value string `json:"value"` - // A description of the constant to explain its purpose on package create or deploy confirmation prompts - Description string `json:"description,omitempty"` - // Whether to automatically indent the variable's value (if multiline) when templating. Based on the number of chars before the start of ###ZARF_CONST_. - AutoIndent bool `json:"autoIndent,omitempty"` - // An optional regex pattern that a constant value must match before a package can be created. - Pattern string `json:"pattern,omitempty"` -} - -// SetVariable tracks internal variables that have been set during this run of Zarf -type SetVariable struct { - Variable `json:",inline"` - // The value the variable is currently set with - Value string `json:"value"` -} - -// Validate runs all validation checks on a package constant. -func (c Constant) Validate() error { - if !regexp.MustCompile(c.Pattern).MatchString(c.Value) { - return fmt.Errorf(lang.PkgValidateErrPkgConstantPattern, c.Name, c.Pattern) - } - - return nil -} diff --git a/src/pkg/variables/variables.go b/src/pkg/variables/variables.go index 1f2e6a8480..929cb13182 100644 --- a/src/pkg/variables/variables.go +++ b/src/pkg/variables/variables.go @@ -7,19 +7,21 @@ package variables import ( "fmt" "regexp" + + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) // SetVariableMap represents a map of variable names to their set values -type SetVariableMap map[string]*SetVariable +type SetVariableMap map[string]*v1alpha1.SetVariable // GetSetVariable gets a variable set within a VariableConfig by its name -func (vc *VariableConfig) GetSetVariable(name string) (variable *SetVariable, ok bool) { +func (vc *VariableConfig) GetSetVariable(name string) (variable *v1alpha1.SetVariable, ok bool) { variable, ok = vc.setVariableMap[name] return variable, ok } // PopulateVariables handles setting the active variables within a VariableConfig's SetVariableMap -func (vc *VariableConfig) PopulateVariables(variables []InteractiveVariable, presetVariables map[string]string) error { +func (vc *VariableConfig) PopulateVariables(variables []v1alpha1.InteractiveVariable, presetVariables map[string]string) error { for name, value := range presetVariables { vc.SetVariable(name, value, false, false, "") } @@ -62,9 +64,9 @@ func (vc *VariableConfig) PopulateVariables(variables []InteractiveVariable, pre } // SetVariable sets a variable in a VariableConfig's SetVariableMap -func (vc *VariableConfig) SetVariable(name, value string, sensitive bool, autoIndent bool, varType VariableType) { - vc.setVariableMap[name] = &SetVariable{ - Variable: Variable{ +func (vc *VariableConfig) SetVariable(name, value string, sensitive bool, autoIndent bool, varType v1alpha1.VariableType) { + vc.setVariableMap[name] = &v1alpha1.SetVariable{ + Variable: v1alpha1.Variable{ Name: name, Sensitive: sensitive, AutoIndent: autoIndent, diff --git a/src/pkg/variables/variables_test.go b/src/pkg/variables/variables_test.go index 901fdf4ba9..07442e97f5 100644 --- a/src/pkg/variables/variables_test.go +++ b/src/pkg/variables/variables_test.go @@ -7,94 +7,96 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) func TestPopulateVariables(t *testing.T) { type test struct { vc VariableConfig - vars []InteractiveVariable + vars []v1alpha1.InteractiveVariable presets map[string]string wantErr bool wantVars SetVariableMap } - prompt := func(_ InteractiveVariable) (value string, err error) { return "Prompt", nil } + prompt := func(_ v1alpha1.InteractiveVariable) (value string, err error) { return "Prompt", nil } tests := []test{ { vc: VariableConfig{setVariableMap: SetVariableMap{}}, - vars: []InteractiveVariable{{Variable: Variable{Name: "NAME"}}}, + vars: []v1alpha1.InteractiveVariable{{Variable: v1alpha1.Variable{Name: "NAME"}}}, presets: map[string]string{}, - wantVars: SetVariableMap{"NAME": {Variable: Variable{Name: "NAME"}}}, + wantVars: SetVariableMap{"NAME": {Variable: v1alpha1.Variable{Name: "NAME"}}}, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME"}, Default: "Default"}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME"}, Default: "Default"}, }, presets: map[string]string{}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME"}, Value: "Default"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME"}, Value: "Default"}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME"}, Default: "Default"}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME"}, Default: "Default"}, }, presets: map[string]string{"NAME": "Set"}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME"}, Value: "Set"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME"}, Value: "Set"}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: FileVariableType}}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: v1alpha1.FileVariableType}}, }, presets: map[string]string{}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: FileVariableType}}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: v1alpha1.FileVariableType}}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: FileVariableType}}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: v1alpha1.FileVariableType}}, }, presets: map[string]string{"NAME": "Set"}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: FileVariableType}, Value: "Set"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: v1alpha1.FileVariableType}, Value: "Set"}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}, prompt: prompt}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME"}, Prompt: true}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME"}, Prompt: true}, }, presets: map[string]string{}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME"}, Value: "Prompt"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME"}, Value: "Prompt"}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}, prompt: prompt}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME"}, Default: "Default", Prompt: true}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME"}, Default: "Default", Prompt: true}, }, presets: map[string]string{}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME"}, Value: "Prompt"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME"}, Value: "Prompt"}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}, prompt: prompt}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME"}, Prompt: true}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME"}, Prompt: true}, }, presets: map[string]string{"NAME": "Set"}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME"}, Value: "Set"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME"}, Value: "Set"}, }, }, } @@ -132,18 +134,18 @@ func TestCheckVariablePattern(t *testing.T) { }, { vc: VariableConfig{ - setVariableMap: SetVariableMap{"NAME": &SetVariable{Value: "name"}}, + setVariableMap: SetVariableMap{"NAME": &v1alpha1.SetVariable{Value: "name"}}, }, name: "NAME", pattern: "n[^a]me", wantErrMsg: "provided value for variable \"NAME\" does not match pattern \"n[^a]me\"", }, { vc: VariableConfig{ - setVariableMap: SetVariableMap{"NAME": &SetVariable{Value: "name"}}, + setVariableMap: SetVariableMap{"NAME": &v1alpha1.SetVariable{Value: "name"}}, }, name: "NAME", pattern: "n[a-z]me", wantErrMsg: "", }, { vc: VariableConfig{ - setVariableMap: SetVariableMap{"NAME": &SetVariable{Value: "name"}}, + setVariableMap: SetVariableMap{"NAME": &v1alpha1.SetVariable{Value: "name"}}, }, name: "NAME", pattern: "n[a-z-bad-pattern", wantErrMsg: "error parsing regexp: missing closing ]: `[a-z-bad-pattern`", }, } diff --git a/zarf.schema.json b/zarf.schema.json index 6249898643..b1fca50208 100644 --- a/zarf.schema.json +++ b/zarf.schema.json @@ -208,6 +208,7 @@ }, "additionalProperties": false, "type": "object", + "description": "Shell represents the desired shell to use for a given command", "patternProperties": { "^x-": {} }