diff --git a/examples/argocd/zarf.yaml b/examples/argocd/zarf.yaml index 8c54846582..daef2cd677 100644 --- a/examples/argocd/zarf.yaml +++ b/examples/argocd/zarf.yaml @@ -35,16 +35,13 @@ components: images: - ghcr.io/stefanprodan/podinfo:6.4.0 actions: - onDeploy: - after: - # This will use a wait action to wait for the pods to be ready - - description: Podinfo pod to be ready via wait action - wait: - cluster: - kind: pod - name: app.kubernetes.io/name=apps-podinfo - namespace: podinfo - condition: ready + - when: AfterDeploy + wait: + cluster: + kind: pod + name: app.kubernetes.io/name=apps-podinfo + namespace: podinfo + condition: ready # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI # The `x-mdx` key is used to render the markdown content for https://docs.zarf.dev/ref/examples diff --git a/examples/big-bang/zarf.yaml b/examples/big-bang/zarf.yaml index 534ebd0ea4..11d2fd9ac0 100644 --- a/examples/big-bang/zarf.yaml +++ b/examples/big-bang/zarf.yaml @@ -17,30 +17,28 @@ components: - name: bigbang required: true actions: - onRemove: - before: - - cmd: | - ./zarf tools kubectl patch helmrelease -n bigbang bigbang --type=merge -p '{"spec":{"suspend":true}}' - ./zarf tools kubectl delete helmrelease -n bigbang istio --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang istio-operator --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang monitoring --ignore-not-found - ./zarf tools kubectl delete providers grafana -n monitoring --ignore-not-found - ./zarf tools kubectl delete alerts grafana -n monitoring --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang promtail --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang loki --ignore-not-found - ./zarf tools kubectl delete kiali -n kiali kiali --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang tempo --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang neuvector --ignore-not-found - ./zarf tools kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io neuvector-validating-crd-webhook --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang kyverno-reporter --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang kyverno-policies --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang kyverno --ignore-not-found - ./zarf tools kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io kyverno-policy-validating-webhook-cfg kyverno-resource-validating-webhook-cfg --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang kiali --ignore-not-found - ./zarf tools kubectl delete helmrelease -n bigbang metrics-server --ignore-not-found - ./zarf tools kubectl delete apiservices.apiregistration.k8s.io -l helm.toolkit.fluxcd.io/namespace=bigbang,helm.toolkit.fluxcd.io/name=metrics-server --ignore-not-found - ./zarf tools kubectl delete gitrepositories -n bigbang -l app.kubernetes.io/part-of=bigbang - description: "Cleaning up Big Bang resources" + - when: BeforeRemove + cmd: | + ./zarf tools kubectl patch helmrelease -n bigbang bigbang --type=merge -p '{"spec":{"suspend":true}}' + ./zarf tools kubectl delete helmrelease -n bigbang istio --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang istio-operator --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang monitoring --ignore-not-found + ./zarf tools kubectl delete providers grafana -n monitoring --ignore-not-found + ./zarf tools kubectl delete alerts grafana -n monitoring --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang promtail --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang loki --ignore-not-found + ./zarf tools kubectl delete kiali -n kiali kiali --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang tempo --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang neuvector --ignore-not-found + ./zarf tools kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io neuvector-validating-crd-webhook --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang kyverno-reporter --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang kyverno-policies --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang kyverno --ignore-not-found + ./zarf tools kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io kyverno-policy-validating-webhook-cfg kyverno-resource-validating-webhook-cfg --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang kiali --ignore-not-found + ./zarf tools kubectl delete helmrelease -n bigbang metrics-server --ignore-not-found + ./zarf tools kubectl delete apiservices.apiregistration.k8s.io -l helm.toolkit.fluxcd.io/namespace=bigbang,helm.toolkit.fluxcd.io/name=metrics-server --ignore-not-found + ./zarf tools kubectl delete gitrepositories -n bigbang -l app.kubernetes.io/part-of=bigbang extensions: bigbang: # renovate: datasource=gitlab-releases depName=big-bang/bigbang versioning=semver registryUrl=https://repo1.dso.mil/ diff --git a/examples/component-actions/zarf.yaml b/examples/component-actions/zarf.yaml index df897d0d99..6d6b913475 100644 --- a/examples/component-actions/zarf.yaml +++ b/examples/component-actions/zarf.yaml @@ -11,126 +11,75 @@ variables: components: - name: on-create actions: - # runs during "zarf package create" - onCreate: - # defaults are applied to all actions in this action set - below are the default defaults - defaults: - dir: "" - env: [] - maxRetries: 0 - maxTotalSeconds: 300 - mute: false - shell: - darwin: sh - linux: sh - windows: powershell - # runs before the component is created - before: - # on Windows with `pwsh` or `powershell`, `touch` is replaced with New-Item - - cmd: touch test-create-before.txt - # description shows a more user friendly message when waiting for the command - description: Create a test file - # dir is the directory to run the command in - dir: "" - # env sets environment variables for this action only - env: - - thing=stuff - # maxRetries is the number of times to retry the action if it fails - maxRetries: 0 - # maxTotalSeconds is the maximum amount of times the action can run before it is killed, over all retries - maxTotalSeconds: 30 - # mute determine if actions output should be printed to the console - mute: false - # shell sets the preferred shell across operating systems, in this case "pwsh" instead of "powershell" on Windows - shell: - windows: pwsh - # runs after the component is created - after: - # actions in a list run in order - - cmd: touch test-create-after.txt - - cmd: sleep 0.5 - - cmd: echo "I can print!" - - cmd: sleep 0.5 - # cmd actions can also specify a multiline string to run like a script - - cmd: | - echo "multiline!" - sleep 0.5 - echo "updates!" - sleep 1 - echo "in!" - sleep 0.5 - echo "realtime!" - sleep 0.5 + - when: BeforeCreate + cmd: touch test-create-before.txt + - when: AfterCreate + cmd: | + touch test-create-after.txt + echo "multiline!" + sleep 0.5 + echo "updates!" + sleep 1 + echo "in!" + sleep 0.5 + echo "realtime!" + sleep 0.5 - name: on-deploy-and-remove actions: - # runs during "zarf package deploy" - onDeploy: - # runs before the component is deployed - before: - - cmd: touch test-deploy-before.txt - # runs after the component is deployed - after: - - cmd: touch test-deploy-after.txt - # runs during "zarf package remove" - onRemove: - # runs before anything else from the component is removed - before: - - cmd: rm test-deploy-before.txt - # runs after everything else from the component is removed - after: - - cmd: rm test-deploy-after.txt + - when: BeforeDeploy + cmd: touch test-deploy-before.txt + - when: AfterDeploy + cmd: touch test-deploy-after.txt + - when: BeforeRemove + cmd: rm test-deploy-before.txt + - when: AfterRemove + cmd: rm test-deploy-after.txt - name: on-deploy-with-variable actions: - # runs during "zarf package deploy" - onDeploy: - # runs before the component is deployed - before: - - cmd: echo "the dog says ${ZARF_VAR_DOG_SOUND}" + - when: BeforeDeploy + cmd: echo "the dog says ${ZARF_VAR_DOG_SOUND}" - name: on-deploy-with-dynamic-variable actions: - # runs during "zarf package deploy" - onDeploy: - # runs before the component is deployed - before: - # setVariables can be used to set a variable for use in other actions or components - - cmd: echo "meow" - # the name of the variable to set with the output of the action (only useable onDeploy) - setVariables: - - name: CAT_SOUND - # this action will have access to the variable set in the previous action - - cmd: echo "the cat says ${ZARF_VAR_CAT_SOUND}" + - when: BeforeDeploy + # setVariables can be used to set a variable for use in other actions or components + cmd: echo "meow" + # the name of the variable to set with the output of the action (only useable on deploy) + setVariables: + - name: CAT_SOUND + - when: BeforeDeploy + # this action will have access to the variable set in the previous action + cmd: echo "the cat says ${ZARF_VAR_CAT_SOUND}" - name: on-deploy-with-multiple-variables actions: - # runs during "zarf package deploy" - onDeploy: - # runs before the component is deployed - before: - # setting this variable will allow it to be used in other actions with additional variables - # set in other actions or components - - cmd: echo "hiss" - # setVariables defines a list of variables to set from the `cmd` standard out. - setVariables: - - name: SNAKE_SOUND - # marks this variable as sensitive to prevent it from being output in the Zarf log - sensitive: true - # autoIndent tells Zarf to maintain spacing for any newlines when templating into a yaml file - autoIndent: true - # onSuccess will only run if steps in this component are successful - onSuccess: - # this action will print the CAT_SOUND variable that was set in a previous component - - cmd: echo "the cat says ${ZARF_VAR_CAT_SOUND}" - # this action will print the DOG_SOUND variable set at the top of the zarf.yaml file - - cmd: echo "the dog says ${ZARF_VAR_DOG_SOUND}" - # this action will print the SNAKE_SOUND variable set within this component - # > NOTE: when including a variable in a command output this will be written to the log regardless of the sensitive setting - # - use `mute` if you want to silence the command output for sensitive variables - - cmd: echo "the snake says ${ZARF_VAR_SNAKE_SOUND}" - # variables are also exposed as TF_VAR_name for terraform, note the lowercase variable name - - cmd: echo "with a TF_VAR, the snake also says ${TF_VAR_snake_sound}" + - when: BeforeDeploy + # setting this variable will allow it to be used in other actions with additional variables + # set in other actions or components + cmd: echo "hiss" + # setVariables defines a list of variables to set from the `cmd` standard out. + setVariables: + - name: SNAKE_SOUND + # marks this variable as sensitive to prevent it from being output in the Zarf log + sensitive: true + # autoIndent tells Zarf to maintain spacing for any newlines when templating into a yaml file + autoIndent: true + # this action will print the CAT_SOUND variable that was set in a previous component + - when: SuccessfulDeploy + cmd: echo "the cat says ${ZARF_VAR_CAT_SOUND}" + # this action will print the DOG_SOUND variable set at the top of the zarf.yaml file + - when: SuccessfulDeploy + cmd: echo "the dog says ${ZARF_VAR_DOG_SOUND}" + # this action will print the SNAKE_SOUND variable set within this component + # > NOTE: when including a variable in a command output this will be written to the log regardless of the sensitive setting + # - use `mute` if you want to silence the command output for sensitive variables + - when: SuccessfulDeploy + cmd: echo "the snake says ${ZARF_VAR_SNAKE_SOUND}" + # variables are also exposed as TF_VAR_name for terraform, note the lowercase variable name + - when: SuccessfulDeploy + cmd: echo "with a TF_VAR, the snake also says ${TF_VAR_snake_sound}" - name: on-deploy-with-template-use-of-variable files: @@ -140,21 +89,6 @@ components: target: test-templated.txt shasum: 3c0404e0c767ace905c361fadded6c4b91fdb88aa07d5c42d2a220a87564836d - - name: on-deploy-with-timeout - description: This component will fail after 1 second - actions: - # runs during "zarf package deploy" - onDeploy: - # defaults allow you to specify default values for the actions in that actionSet - defaults: - # maxTotalSeconds is the maximum amount of time the action can run before it is killed, over all retries - maxTotalSeconds: 1 - before: - # this action will fail after 1 second - - cmd: sleep 10 - onFailure: - - cmd: echo "😭😭😭 this action failed because it took too long to run 😭😭😭" - - name: on-remove # A manifest that we expect to be removed by Zarf manifests: @@ -162,37 +96,31 @@ components: files: - test-configmap.yaml actions: - # runs during "zarf package remove" - onRemove: - before: - # because this runs before the manifest is removed this should return our manifest - - cmd: ./zarf tools kubectl get configmap -n zarf remove-test-configmap || echo "Not Found" - after: - # because this runs after the manifest is removed this should no longer be found - - cmd: ./zarf tools kubectl get configmap -n zarf remove-test-configmap || echo "Not Found" + - when: BeforeRemove + # because this runs before the manifest is removed this should return our manifest + cmd: ./zarf tools kubectl get configmap -n zarf remove-test-configmap || echo "Not Found" + - when: AfterRemove + # because this runs after the manifest is removed this should no longer be found + cmd: ./zarf tools kubectl get configmap -n zarf remove-test-configmap || echo "Not Found" - name: on-deploy-with-env-var actions: - onDeploy: - before: - - cmd: touch $ZARF_VAR_TEST_FILENAME - env: - # this will set the env var ZARF_VAR_TEST_FILENAME - useful for passing information into scripts - - ZARF_VAR_TEST_FILENAME=test-filename-from-env.txt + - when: BeforeDeploy + cmd: touch $ZARF_VAR_TEST_FILENAME + env: + # this will set the env var ZARF_VAR_TEST_FILENAME - useful for passing information into scripts + ZARF_VAR_TEST_FILENAME: test-filename-from-env.txt - name: on-create-with-network-wait-action description: This component will wait for 15 seconds for a network resource to be available actions: - onCreate: - after: - - description: Github.com to be available - maxTotalSeconds: 15 - wait: - # wait for a network address to return a 200 OK response - network: - protocol: https - address: github.com - code: 200 + - when: AfterCreate + wait: + # wait for a network address to return a 200 OK response + network: + protocol: https + address: github.com + code: 200 - name: on-deploy-with-wait-action description: This component will wait for 5 seconds for the test-configmap to be exist @@ -201,26 +129,22 @@ components: files: - test-configmap.yaml actions: - onDeploy: - after: - - description: The simple-configmap to exist - maxTotalSeconds: 5 - wait: - # wait for the configmap to be available in the cluster - cluster: - kind: configmap - name: simple-configmap - namespace: zarf + - when: AfterDeploy + wait: + # wait for the configmap to be available in the cluster + cluster: + kind: configmap + name: simple-configmap + namespace: zarf - name: on-deploy-immediate-failure description: This component will fail on the first error instead of continuing execution # the default for multi-line commands is set -e actions: - onDeploy: - after: - - cmd: | - bad_cmd - echo "this text shouldn't be printed" + - when: AfterDeploy + cmd: | + bad_cmd + echo "this text shouldn't be printed" # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI # The `x-mdx` key is used to render the markdown content for https://docs.zarf.dev/ref/examples diff --git a/examples/composable-packages/zarf.yaml b/examples/composable-packages/zarf.yaml index 35dab0b33e..2b81b42479 100644 --- a/examples/composable-packages/zarf.yaml +++ b/examples/composable-packages/zarf.yaml @@ -36,9 +36,8 @@ components: name: baseline # Un'name'd Zarf primitives will be appended to the end of the primitive's list for that component. actions: - onDeploy: - before: - - cmd: ./zarf tools kubectl get -n dos-games deployment -o jsonpath={.items[0].metadata.creationTimestamp} + - when: BeforeDeploy + cmd: ./zarf tools kubectl get -n dos-games deployment -o jsonpath={.items[0].metadata.creationTimestamp} # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI # The `x-mdx` key is used to render the markdown content for https://docs.zarf.dev/ref/examples diff --git a/examples/dos-games/zarf.yaml b/examples/dos-games/zarf.yaml index 922f402e76..6a1109101c 100644 --- a/examples/dos-games/zarf.yaml +++ b/examples/dos-games/zarf.yaml @@ -16,14 +16,13 @@ components: images: - defenseunicorns/zarf-game:multi-tile-dark actions: - onDeploy: - after: - - wait: - cluster: - kind: deployment - name: game - namespace: dos-games - condition: available + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: game + namespace: dos-games + condition: available # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI # The `x-mdx` key is used to render the markdown content for https://docs.zarf.dev/ref/examples diff --git a/examples/helm-charts/zarf.yaml b/examples/helm-charts/zarf.yaml index e5e3757717..243f9a5b7b 100644 --- a/examples/helm-charts/zarf.yaml +++ b/examples/helm-charts/zarf.yaml @@ -57,32 +57,34 @@ components: # This is the cosign signature for the podinfo image for image signature verification - ghcr.io/stefanprodan/podinfo:sha256-57a654ace69ec02ba8973093b6a786faa15640575fbf0dbb603db55aca2ccec8.sig actions: - onDeploy: - after: - - wait: - cluster: - kind: deployment - name: podinfo-local - namespace: podinfo-from-local-chart - condition: available - - wait: - cluster: - kind: deployment - name: podinfo-oci - namespace: podinfo-from-oci - condition: available - - wait: - cluster: - kind: deployment - name: podinfo-git - namespace: podinfo-from-git - condition: available - - wait: - cluster: - kind: deployment - name: cool-release-name-podinfo - namespace: podinfo-from-repo - condition: available + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: podinfo-local + namespace: podinfo-from-local-chart + condition: available + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: podinfo-oci + namespace: podinfo-from-oci + condition: available + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: podinfo-git + namespace: podinfo-from-git + condition: available + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: cool-release-name-podinfo + namespace: podinfo-from-repo + condition: available # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI # The `x-mdx` key is used to render the markdown content for https://docs.zarf.dev/ref/examples diff --git a/examples/kiwix/zarf.yaml b/examples/kiwix/zarf.yaml index bb91def028..fb23d7db43 100644 --- a/examples/kiwix/zarf.yaml +++ b/examples/kiwix/zarf.yaml @@ -30,13 +30,12 @@ components: path: /data compress: true actions: - onCreate: - before: - # Download a .zim file of a DevOps Stack Exchange snapshot into the data directory for use with Kiwix - - cmd: curl https://zarf-remote.s3.us-east-2.amazonaws.com/testdata/devops.stackexchange.com_en_all_2023-05.zim -o zim-data/devops.stackexchange.com_en_all_2023-05.zim - # Below are some more examples of *.zim files of available content: - # https://library.kiwix.org/?lang=eng - # NOTE: If `zarf package create`ing regularly you should mirror content to a web host you control to be a friendly neighbor + - when: BeforeCreate + # Download a .zim file of a DevOps Stack Exchange snapshot into the data directory for use with Kiwix + cmd: curl https://zarf-remote.s3.us-east-2.amazonaws.com/testdata/devops.stackexchange.com_en_all_2023-05.zim -o zim-data/devops.stackexchange.com_en_all_2023-05.zim + # Below are some more examples of *.zim files of available content: + # https://library.kiwix.org/?lang=eng + # NOTE: If `zarf package create`ing regularly you should mirror content to a web host you control to be a friendly neighbor # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI # The `x-mdx` key is used to render the markdown content for https://docs.zarf.dev/ref/examples diff --git a/examples/longhorn/zarf.yaml b/examples/longhorn/zarf.yaml index 5aa299d573..dc8c446272 100644 --- a/examples/longhorn/zarf.yaml +++ b/examples/longhorn/zarf.yaml @@ -17,24 +17,22 @@ components: executable: true actions: # Run the Longhorn Environment Check on this cluster's nodes. - onDeploy: - after: - - cmd: | - export PATH=$PATH:./ - awk '{gsub(/kubectl /, "./zarf tools kubectl ")} 1' ./environment_check.sh > tmp - mv tmp ./environment_check.sh - awk '{gsub(/"kubectl" /, "")} 1' ./environment_check.sh > tmp - mv tmp ./environment_check.sh - chmod +x ./environment_check.sh - ./environment_check.sh + - when: AfterDeploy + cmd: | + export PATH=$PATH:./ + awk '{gsub(/kubectl /, "./zarf tools kubectl ")} 1' ./environment_check.sh > tmp + mv tmp ./environment_check.sh + awk '{gsub(/"kubectl" /, "")} 1' ./environment_check.sh > tmp + mv tmp ./environment_check.sh + chmod +x ./environment_check.sh + ./environment_check.sh - name: longhorn required: true description: Deploy Longhorn into a Kubernetes cluster. https://longhorn.io actions: # Set the delete confirmation flag for Longhorn - onRemove: - before: - - cmd: './zarf tools kubectl -n longhorn-system patch -p ''{"value": "true"}'' --type=merge lhs deleting-confirmation-flag' + - when: BeforeRemove + cmd: './zarf tools kubectl -n longhorn-system patch -p ''{"value": "true"}'' --type=merge lhs deleting-confirmation-flag' manifests: - name: longhorn-connect namespace: longhorn-system diff --git a/examples/manifests/zarf.yaml b/examples/manifests/zarf.yaml index 092f6ca684..55adbcc54a 100644 --- a/examples/manifests/zarf.yaml +++ b/examples/manifests/zarf.yaml @@ -13,16 +13,15 @@ components: # local manifests are specified relative to the `zarf.yaml` that uses them: - httpd-deployment.yaml actions: - onDeploy: + - when: AfterDeploy # the following checks were computed by viewing the success state of the package deployment # and creating `wait` actions that match - after: - - wait: - cluster: - kind: deployment - name: httpd-deployment - namespace: httpd - condition: "{.status.readyReplicas}=2" + wait: + cluster: + kind: deployment + name: httpd-deployment + namespace: httpd + condition: "{.status.readyReplicas}=2" # image discovery is supported in all manifests and charts using: # zarf prepare find-images images: @@ -39,16 +38,13 @@ components: # this sha256 can be discovered using: # zarf prepare sha256sum https://k8s.io/examples/application/deployment.yaml actions: - onDeploy: - # the following checks were computed by viewing the success state of the package deployment - # and creating `wait` actions that match - after: - - wait: - cluster: - kind: deployment - name: nginx-deployment - namespace: nginx - condition: available + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: nginx-deployment + namespace: nginx + condition: available # image discovery is supported in all manifests and charts using: # zarf prepare find-images images: @@ -65,16 +61,13 @@ components: # while ?ref= is not a requirement, it is recommended to use a specific commit hash / git tag to # ensure that the kustomization is not changed in a way that breaks your deployment. actions: - onDeploy: - # the following checks were computed by viewing the success state of the package deployment - # and creating `wait` actions that match - after: - - wait: - cluster: - kind: deployment - name: podinfo - namespace: podinfo - condition: available + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: podinfo + namespace: podinfo + condition: available # image discovery is supported in all manifests and charts using: # zarf prepare find-images images: diff --git a/examples/package-flavors/zarf.yaml b/examples/package-flavors/zarf.yaml index cbb735e66d..0ca15f41b3 100644 --- a/examples/package-flavors/zarf.yaml +++ b/examples/package-flavors/zarf.yaml @@ -13,11 +13,10 @@ components: images: - rockylinux:9-minimal actions: - onDeploy: - before: - - cmd: echo "rockylinux:9-minimal" - setVariables: - - name: IMAGE + - when: BeforeDeploy + cmd: echo "rockylinux:9-minimal" + setVariables: + - name: IMAGE - name: image required: true @@ -27,11 +26,10 @@ components: images: - oraclelinux:9-slim actions: - onDeploy: - before: - - cmd: echo "oraclelinux:9-slim" - setVariables: - - name: IMAGE + - when: BeforeDeploy + cmd: echo "oraclelinux:9-slim" + setVariables: + - name: IMAGE - name: image required: true @@ -41,11 +39,10 @@ components: images: - almalinux:9-minimal actions: - onDeploy: - before: - - cmd: echo "almalinux:9-minimal" - setVariables: - - name: IMAGE + - when: BeforeDeploy + cmd: echo "almalinux:9-minimal" + setVariables: + - name: IMAGE - name: image required: true @@ -55,11 +52,10 @@ components: images: - opensuse/leap:15 actions: - onDeploy: - before: - - cmd: echo "opensuse/leap:15" - setVariables: - - name: IMAGE + - when: BeforeDeploy + cmd: echo "opensuse/leap:15" + setVariables: + - name: IMAGE - name: pod description: "The pod that runs the specified flavor of Enterprise Linux" diff --git a/examples/podinfo-flux/zarf.yaml b/examples/podinfo-flux/zarf.yaml index 4ab41b360e..b552ee1c5d 100644 --- a/examples/podinfo-flux/zarf.yaml +++ b/examples/podinfo-flux/zarf.yaml @@ -82,16 +82,13 @@ components: # Note: this is a flux kustomize OCI artifact rather than a container image - ghcr.io/stefanprodan/manifests/podinfo:6.4.0 actions: - onDeploy: - after: - # This will use a wait action to wait for the podinfo pod to be ready - - description: Podinfo pods to be ready via wait action - wait: - cluster: - kind: pod - name: app=podinfo - namespace: podinfo-oci - condition: ready + - when: AfterDeploy + wait: + cluster: + kind: pod + name: app=podinfo + namespace: podinfo + condition: ready # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI # The `x-mdx` key is used to render the markdown content for https://docs.zarf.dev/ref/examples diff --git a/examples/variables/zarf.yaml b/examples/variables/zarf.yaml index df2873b1c4..e679b6227f 100644 --- a/examples/variables/zarf.yaml +++ b/examples/variables/zarf.yaml @@ -55,18 +55,17 @@ components: - source: simple-terraform.tf target: modified-terraform.tf actions: - onDeploy: - after: - # This command uses Zarf to return the SHASUM of the terraform file (`type: file` variables will return the filepath instead of the contents when used in actions due to constraints on env var size) - - cmd: ./zarf prepare sha256sum ${ZARF_VAR_MODIFIED_TERRAFORM} + - when: AfterDeploy + # This command uses Zarf to return the SHASUM of the terraform file (`type: file` variables will return the filepath instead of the contents when used in actions due to constraints on env var size) + cmd: ./zarf prepare sha256sum ${ZARF_VAR_MODIFIED_TERRAFORM} # `mute` is set to exclude the command output from being shown (since we are treating it as sensitive below) - mute: true - setVariables: - - name: MODIFIED_TERRAFORM_SHASUM - # `sensitive` is set to exclude the command output from the logs - sensitive: true - # `pattern` here will ensure that we get a properly formatted sha256 sum back from the zarf prepare command - pattern: "^[\\da-f]{64}$" + mute: true + setVariables: + - name: MODIFIED_TERRAFORM_SHASUM + # `sensitive` is set to exclude the command output from the logs + sensitive: true + # `pattern` here will ensure that we get a properly formatted sha256 sum back from the zarf prepare command + pattern: "^[\\da-f]{64}$" # The following component deploys nginx to the cluster using the defined variables - name: variables-with-nginx @@ -82,14 +81,13 @@ components: - nginx-deployment.yaml - nginx-service.yaml actions: - onDeploy: - after: - - wait: - cluster: - kind: pod - namespace: nginx - name: app=nginx - condition: Ready + - when: AfterDeploy + wait: + cluster: + kind: pod + namespace: nginx + name: app=nginx + condition: Ready # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI # The `x-mdx` key is used to render the markdown content for https://docs.zarf.dev/ref/examples diff --git a/examples/yolo/zarf.yaml b/examples/yolo/zarf.yaml index dfa4cf65f1..c2fbde28b3 100644 --- a/examples/yolo/zarf.yaml +++ b/examples/yolo/zarf.yaml @@ -14,14 +14,13 @@ components: - ../dos-games/manifests/deployment.yaml - ../dos-games/manifests/service.yaml actions: - onDeploy: - after: - - wait: - cluster: - kind: deployment - name: game - namespace: zarf-yolo-example - condition: available + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: game + namespace: zarf-yolo-example + condition: available # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI # The `x-mdx` key is used to render the markdown content for https://docs.zarf.dev/ref/examples diff --git a/packages/distros/eks/zarf.yaml b/packages/distros/eks/zarf.yaml index 8172dfdf60..142f6c65f6 100644 --- a/packages/distros/eks/zarf.yaml +++ b/packages/distros/eks/zarf.yaml @@ -50,20 +50,20 @@ components: - name: deploy-eks-cluster description: Create an EKS cluster! actions: - onDeploy: - before: - - cmd: ./binaries/eksctl_$(uname -s)_$(uname -m) create cluster --dry-run -f eks.yaml - - cmd: sleep 15 - - cmd: ./binaries/eksctl_$(uname -s)_$(uname -m) create cluster -f eks.yaml - - cmd: ./binaries/eksctl_$(uname -s)_$(uname -m) utils write-kubeconfig -c ${ZARF_VAR_EKS_CLUSTER_NAME} + - when: BeforeDeploy + cmd: | + ./binaries/eksctl_$(uname -s)_$(uname -m) create cluster --dry-run -f eks.yaml + sleep 15 + ./binaries/eksctl_$(uname -s)_$(uname -m) create cluster -f eks.yaml + ./binaries/eksctl_$(uname -s)_$(uname -m) utils write-kubeconfig -c ${ZARF_VAR_EKS_CLUSTER_NAME} - name: teardown-eks-cluster description: Delete the EKS cluster that this package was used to create. actions: - onDeploy: - before: - - cmd: ./binaries/eksctl_$(uname -s)_$(uname -m) delete cluster -f eks.yaml --disable-nodegroup-eviction --wait - after: - # clean up after ourselves - - cmd: rm -rf binaries - - cmd: rm -f eks.yaml + - when: BeforeDeploy + cmd: ./binaries/eksctl_$(uname -s)_$(uname -m) delete cluster -f eks.yaml --disable-nodegroup-eviction --wait + - when: AfterDeploy + # clean up after ourselves + cmd: | + rm -rf binaries + rm -f eks.yaml diff --git a/packages/distros/k3s/common/zarf.yaml b/packages/distros/k3s/common/zarf.yaml index 0a2b6b287e..f9834d5861 100644 --- a/packages/distros/k3s/common/zarf.yaml +++ b/packages/distros/k3s/common/zarf.yaml @@ -17,32 +17,25 @@ components: K3s provides the cluster need for Zarf running in Appliance Mode as well as can host a low-resource Gitops Service if not using an existing Kubernetes platform. actions: - onDeploy: - defaults: - maxRetries: 5 - before: - - cmd: ./zarf internal is-valid-hostname - maxRetries: 0 - description: Check if the current system has a, RFC1123 compliant hostname - # If running RHEL variant, disable firewalld - # https://rancher.com/docs/k3s/latest/en/advanced/#additional-preparation-for-red-hat-centos-enterprise-linux - # NOTE: The empty echo prevents infinite retry loops on non-RHEL systems where the exit code would be an error - - cmd: "[ -e /etc/redhat-release ] && systemctl disable firewalld --now || echo ''" - description: If running a RHEL variant, disable 'firewalld' per k3s docs - after: - # Configure K3s systemd service - - cmd: systemctl daemon-reload - description: Reload the system services - - cmd: systemctl enable k3s - description: Enable 'k3s' to run at system boot - - cmd: systemctl restart k3s - description: Start the 'k3s' system service - onRemove: - before: - - cmd: /opt/zarf/zarf-clean-k3s.sh - description: Remove 'k3s' from the system - - cmd: rm /opt/zarf/zarf-clean-k3s.sh - description: Remove the cleanup script + - when: BeforeDeploy + cmd: ./zarf internal is-valid-hostname + # If running RHEL variant, disable firewalld + # https://rancher.com/docs/k3s/latest/en/advanced/#additional-preparation-for-red-hat-centos-enterprise-linux + # NOTE: The empty echo prevents infinite retry loops on non-RHEL systems where the exit code would be an error + - when: BeforeDeploy + cmd: "[ -e /etc/redhat-release ] && systemctl disable firewalld --now || echo ''" + # Configure K3s systemd service + - when: AfterDeploy + cmd: systemctl daemon-reload + - when: AfterDeploy + cmd: systemctl enable k3s + - when: AfterDeploy + cmd: systemctl restart k3s + - when: BeforeRemove + cmd: /opt/zarf/zarf-clean-k3s.sh + - when: BeforeRemove + cmd: rm /opt/zarf/zarf-clean-k3s.sh + files: # K3s removal script - source: zarf-clean-k3s.sh diff --git a/packages/distros/k3s/zarf.yaml b/packages/distros/k3s/zarf.yaml index 0813b1ee38..d0352e51c8 100644 --- a/packages/distros/k3s/zarf.yaml +++ b/packages/distros/k3s/zarf.yaml @@ -28,11 +28,8 @@ components: shasum: bc4d05bad56a583c80ff443d60e8277a136cc4357dc8527702d38b5cca28880d target: /var/lib/rancher/k3s/agent/images/k3s.tar.zst actions: - onDeploy: - before: - - cmd: if [ "$(uname -m)" != "x86_64" ]; then echo "this package architecture is amd64, but the target system has a different architecture. These architectures must be the same" && exit 1; fi - description: Check that the host architecture matches the package architecture - maxRetries: 0 + - cmd: if [ "$(uname -m)" != "x86_64" ]; then echo "this package architecture is amd64, but the target system has a different architecture. These architectures must be the same" && exit 1; fi + when: BeforeDeploy # ARM-64 version of the K3s stack - name: k3s @@ -58,8 +55,5 @@ components: shasum: 50621ae1391aec7fc66ca66a46a0e9fd48ce373a58073000efdc278233adc64b target: /var/lib/rancher/k3s/agent/images/k3s.tar.zst actions: - onDeploy: - before: - - cmd: if [ "$(uname -m)" != "aarch64" ] && [ "$(uname -m)" != "arm64" ]; then echo "this package architecture is arm64, but the target system has a different architecture. These architectures must be the same" && exit 1; fi - description: Check that the host architecture matches the package architecture - maxRetries: 0 + - cmd: if [ "$(uname -m)" != "aarch64" ] && [ "$(uname -m)" != "arm64" ]; then echo "this package architecture is arm64, but the target system has a different architecture. These architectures must be the same" && exit 1; fi + when: BeforeDeploy diff --git a/packages/gitea/zarf.yaml b/packages/gitea/zarf.yaml index 2f59bebdba..15720a3035 100644 --- a/packages/gitea/zarf.yaml +++ b/packages/gitea/zarf.yaml @@ -64,30 +64,23 @@ components: valuesFiles: - gitea-values.yaml actions: - onDeploy: - before: - - cmd: ./zarf internal update-gitea-pvc --no-progress - setVariables: - - name: GIT_SERVER_CREATE_PVC - mute: true - after: - - wait: - cluster: - kind: pod - namespace: zarf - name: app=gitea - condition: Ready - - cmd: ./zarf internal create-read-only-gitea-user --no-progress - maxRetries: 3 - maxTotalSeconds: 60 - description: Create the read-only Gitea user - - cmd: ./zarf internal create-artifact-registry-token --no-progress - maxRetries: 3 - maxTotalSeconds: 60 - description: Create an artifact registry token - - onFailure: - - cmd: ./zarf internal update-gitea-pvc --rollback --no-progress + - when: BeforeDeploy + cmd: ./zarf internal update-gitea-pvc --no-progress + setVariables: + - name: GIT_SERVER_CREATE_PVC + - when: AfterDeploy + wait: + cluster: + kind: pod + namespace: zarf + name: app=gitea + condition: Ready + - when: AfterDeploy + cmd: ./zarf internal create-read-only-gitea-user --no-progress + - when: AfterDeploy + cmd: ./zarf internal create-artifact-registry-token --no-progress + - when: FailedDeploy + cmd: ./zarf internal update-gitea-pvc --rollback --no-progress # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI x-mdx: | diff --git a/packages/logging-pgl/zarf.yaml b/packages/logging-pgl/zarf.yaml new file mode 100644 index 0000000000..8cae9354a4 --- /dev/null +++ b/packages/logging-pgl/zarf.yaml @@ -0,0 +1,50 @@ +kind: ZarfPackageConfig +metadata: + name: init-package-logging + +components: + - name: logging + description: | + Deploys the Promtail Grafana & Loki (PGL) stack. + Aggregates logs from different containers and presents them in a web dashboard. + Recommended if no other logging stack is deployed in the cluster. + images: + - docker.io/grafana/promtail:2.9.2 + - grafana/grafana:8.3.5 + - grafana/loki:2.6.1 + - quay.io/kiwigrid/k8s-sidecar:1.19.2 + manifests: + - name: logging-connect + namespace: zarf + files: + - connect.yaml + charts: + - name: loki-stack + releaseName: zarf-loki-stack + url: https://grafana.github.io/helm-charts + version: 2.10.1 + namespace: zarf + valuesFiles: + - pgl-values.yaml + actions: + - when: AfterDeploy + wait: + cluster: + kind: pod + namespace: zarf + name: app=loki + condition: Ready + - when: AfterDeploy + wait: + cluster: + kind: pod + namespace: zarf + name: app.kubernetes.io/name=grafana + condition: Ready + - when: AfterDeploy + wait: + cluster: + kind: pod + namespace: zarf + name: app.kubernetes.io/name=promtail + condition: Ready diff --git a/packages/zarf-agent/zarf.yaml b/packages/zarf-agent/zarf.yaml index 65ee63170f..3122048e17 100644 --- a/packages/zarf-agent/zarf.yaml +++ b/packages/zarf-agent/zarf.yaml @@ -33,18 +33,12 @@ components: - manifests/clusterrolebinding.yaml - manifests/serviceaccount.yaml actions: - onCreate: - before: - - cmd: "test \"###ZARF_PKG_TMPL_AGENT_IMAGE_TAG###\" != \"local\" || make build-local-agent-image AGENT_IMAGE_TAG=\"###ZARF_PKG_TMPL_AGENT_IMAGE_TAG###\" ARCH=\"###ZARF_PKG_ARCH###\"" - shell: - windows: pwsh - dir: ../.. - description: Build the local agent image (if 'AGENT_IMAGE_TAG' was specified as 'local') - onDeploy: - after: - - wait: - cluster: - kind: pod - namespace: zarf - name: app=agent-hook - condition: Ready + - when: BeforeCreate + cmd: "test \"###ZARF_PKG_TMPL_AGENT_IMAGE_TAG###\" != \"local\" || make build-local-agent-image AGENT_IMAGE_TAG=\"###ZARF_PKG_TMPL_AGENT_IMAGE_TAG###\" ARCH=\"###ZARF_PKG_ARCH###\"" + - when: AfterDeploy + wait: + cluster: + kind: pod + namespace: zarf + name: app=agent-hook + condition: Ready diff --git a/packages/zarf-registry/zarf.yaml b/packages/zarf-registry/zarf.yaml index 2da03ccda6..d8b52a7ae2 100644 --- a/packages/zarf-registry/zarf.yaml +++ b/packages/zarf-registry/zarf.yaml @@ -172,11 +172,10 @@ components: # This image (or images) must match that used for injection (see zarf-config.toml) - "###ZARF_PKG_TMPL_REGISTRY_IMAGE_DOMAIN######ZARF_PKG_TMPL_REGISTRY_IMAGE###:###ZARF_PKG_TMPL_REGISTRY_IMAGE_TAG###" actions: - onDeploy: - after: - - wait: - cluster: - kind: deployment - namespace: zarf - name: app=docker-registry - condition: Available + - when: AfterDeploy + wait: + cluster: + kind: deployment + namespace: zarf + name: app=docker-registry + condition: Available diff --git a/src/extensions/bigbang/bigbang.go b/src/extensions/bigbang/bigbang.go index b89b61f460..9761184b70 100644 --- a/src/extensions/bigbang/bigbang.go +++ b/src/extensions/bigbang/bigbang.go @@ -137,20 +137,10 @@ func Run(YOLO bool, tmpPaths *layout.ComponentPaths, c types.ZarfComponent) (typ return c, fmt.Errorf("unable to sort Big Bang HelmReleases: %w", err) } - // ten minutes in seconds - maxTotalSeconds := 10 * 60 - - defaultMaxTotalSeconds := c.Actions.OnDeploy.Defaults.MaxTotalSeconds - if defaultMaxTotalSeconds > maxTotalSeconds { - maxTotalSeconds = defaultMaxTotalSeconds - } - // Add wait actions for each of the helm releases in generally the order they should be deployed. for _, hrNamespacedName := range namespacedHelmReleaseNames { hr := hrDependencies[hrNamespacedName] action := types.ZarfComponentAction{ - Description: fmt.Sprintf("Big Bang Helm Release `%s` to be ready", hrNamespacedName), - MaxTotalSeconds: &maxTotalSeconds, Wait: &types.ZarfComponentActionWait{ Cluster: &types.ZarfComponentActionWaitCluster{ Kind: "HelmRelease", @@ -166,7 +156,6 @@ func Run(YOLO bool, tmpPaths *layout.ComponentPaths, c types.ZarfComponent) (typ // may not ever be created. See links below for more details. // https://repo1.dso.mil/big-bang/bigbang/-/blob/1.54.0/chart/templates/metrics-server/helmrelease.yaml if hr.Metadata.Name == "metrics-server" { - action.Description = "K8s metric server to exist or be deployed by Big Bang" action.Wait.Cluster = &types.ZarfComponentActionWaitCluster{ Kind: "APIService", // https://github.com/kubernetes-sigs/metrics-server#compatibility-matrix @@ -174,7 +163,7 @@ func Run(YOLO bool, tmpPaths *layout.ComponentPaths, c types.ZarfComponent) (typ } } - c.Actions.OnDeploy.OnSuccess = append(c.Actions.OnDeploy.OnSuccess, action) + c.Actions = append(c.Actions, action) } t := true @@ -192,25 +181,26 @@ func Run(YOLO bool, tmpPaths *layout.ComponentPaths, c types.ZarfComponent) (typ "get events -A", } - // Add onFailure actions with additional troubleshooting information. + // Add FailedDeploy actions with additional troubleshooting information. for _, cmd := range failureGeneral { - c.Actions.OnDeploy.OnFailure = append(c.Actions.OnDeploy.OnFailure, types.ZarfComponentAction{ - Cmd: fmt.Sprintf("./zarf tools kubectl %s", cmd), + c.Actions = append(c.Actions, types.ZarfComponentAction{ + Cmd: fmt.Sprintf("./zarf tools kubectl %s", cmd), + When: types.FailedDeploy, }) } for _, cmd := range failureDebug { - c.Actions.OnDeploy.OnFailure = append(c.Actions.OnDeploy.OnFailure, types.ZarfComponentAction{ - Mute: &t, - Description: "Storing debug information to the log for troubleshooting.", - Cmd: fmt.Sprintf("./zarf tools kubectl %s", cmd), + c.Actions = append(c.Actions, types.ZarfComponentAction{ + Mute: &t, + Cmd: fmt.Sprintf("./zarf tools kubectl %s", cmd), + When: types.FailedDeploy, }) } - // Add a pre-remove action to suspend the Big Bang HelmReleases to prevent reconciliation during removal. - c.Actions.OnRemove.Before = append(c.Actions.OnRemove.Before, types.ZarfComponentAction{ - Description: "Suspend Big Bang HelmReleases to prevent reconciliation during removal.", - Cmd: `./zarf tools kubectl patch helmrelease -n bigbang bigbang --type=merge -p '{"spec":{"suspend":true}}'`, + // Add a BeforeRemove action to suspend the Big Bang HelmReleases to prevent reconciliation during removal. + c.Actions = append(c.Actions, types.ZarfComponentAction{ + Cmd: `./zarf tools kubectl patch helmrelease -n bigbang bigbang --type=merge -p '{"spec":{"suspend":true}}'`, + When: types.BeforeRemove, }) // Select the images needed to support the repos for this configuration of Big Bang. diff --git a/src/extensions/bigbang/test/package/zarf.yaml b/src/extensions/bigbang/test/package/zarf.yaml index 426fed8f54..acd7063d42 100644 --- a/src/extensions/bigbang/test/package/zarf.yaml +++ b/src/extensions/bigbang/test/package/zarf.yaml @@ -27,7 +27,7 @@ components: - disable-all-bb###ZARF_PKG_TMPL_BB_MAJOR###.yaml - enable-twistlock.yaml actions: - onDeploy: - onFailure: - - cmd: ./zarf tools kubectl describe nodes - - cmd: ./zarf tools kubectl describe pods -A + - when: FailedDeploy + cmd: ./zarf tools kubectl describe nodes + - when: FailedDeploy + cmd: ./zarf tools kubectl describe pods -A diff --git a/src/pkg/packager/actions/actions.go b/src/pkg/packager/actions/actions.go index 2f01757d6c..642b80415a 100644 --- a/src/pkg/packager/actions/actions.go +++ b/src/pkg/packager/actions/actions.go @@ -5,298 +5,147 @@ package actions import ( + "bytes" "context" + "errors" "fmt" - "regexp" + "io" + "os" + "os/exec" + "path/filepath" "runtime" "strings" - "time" - "github.com/defenseunicorns/pkg/helpers/v2" - "github.com/defenseunicorns/zarf/src/internal/packager/template" - "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/utils" - "github.com/defenseunicorns/zarf/src/pkg/utils/exec" "github.com/defenseunicorns/zarf/src/pkg/variables" "github.com/defenseunicorns/zarf/src/types" ) -// Run runs all provided actions. -func Run(defaultCfg types.ZarfComponentActionDefaults, actions []types.ZarfComponentAction, variableConfig *variables.VariableConfig) error { - if variableConfig == nil { - variableConfig = template.GetZarfVariableConfig() - } +type runOptions struct { + action types.ZarfComponentAction + vc *variables.VariableConfig +} - for _, a := range actions { - if err := runAction(defaultCfg, a, variableConfig); err != nil { - return err +func Run(ctx context.Context, actions []types.ZarfComponentAction, actionPhase types.ActionPhase, vc *variables.VariableConfig) error { + for _, action := range actions { + if action.Phase() == actionPhase { + err := run(ctx, runOptions{action, vc}) + if err != nil { + return err + } } } return nil } -// Run commands that a component has provided. -func runAction(defaultCfg types.ZarfComponentActionDefaults, action types.ZarfComponentAction, variableConfig *variables.VariableConfig) error { - var ( - ctx context.Context - cancel context.CancelFunc - cmdEscaped string - out string - err error +func run(ctx context.Context, opts runOptions) (err error) { + action := opts.action + vc := opts.vc + + if action.Cmd == "" && action.Wait == nil { + return errors.New("invalid action. The command must be a non-empty string or a wait action must be used") + } - cmd = action.Cmd - ) + var cmdargs []string + switch runtime.GOOS { + case "windows": + cmdargs = []string{"cmd", "/C"} + default: + cmdargs = []string{"/bin/sh", "-c", "-e"} + } - // If the action is a wait, convert it to a command. - if action.Wait != nil { - // If the wait has no timeout, set a default of 5 minutes. - if action.MaxTotalSeconds == nil { - fiveMin := 300 - action.MaxTotalSeconds = &fiveMin - } + if len(action.Interpreter) > 0 { + cmdargs = action.Interpreter + } - // Convert the wait to a command. - if cmd, err = convertWaitToCmd(*action.Wait, action.MaxTotalSeconds); err != nil { + if action.Wait != nil { + action.Cmd, err = convertWaitToCmd(action.Wait) + if err != nil { return err } - - // Mute the output because it will be noisy. - t := true - action.Mute = &t - - // Set the max retries to 0. - z := 0 - action.MaxRetries = &z - - // Not used for wait actions. - d := "" - action.Dir = &d - action.Env = []string{} - action.SetVariables = []variables.Variable{} } - if action.Description != "" { - cmdEscaped = action.Description - } else { - cmdEscaped = helpers.Truncate(cmd, 60, false) + // Patch the zarf binary path + zarfCommand, err := utils.GetFinalExecutableCommand() + if err != nil { + return err } + action.Cmd = strings.ReplaceAll(action.Cmd, "./zarf ", zarfCommand+" ") - spinner := message.NewProgressSpinner("Running \"%s\"", cmdEscaped) - // Persist the spinner output so it doesn't get overwritten by the command output. - spinner.EnablePreserveWrites() + cmdargs = append(cmdargs, action.Cmd) - actionDefaults := actionGetCfg(defaultCfg, action, variableConfig.GetAllTemplates()) - - if cmd, err = actionCmdMutation(cmd, actionDefaults.Shell); err != nil { - spinner.Errorf(err, "Error mutating command: %s", cmdEscaped) + cmdEnv := os.Environ() + for k, v := range action.Env { + cmdEnv = append(cmdEnv, fmt.Sprintf("%s=%s", k, v)) } - duration := time.Duration(actionDefaults.MaxTotalSeconds) * time.Second - timeout := time.After(duration) - - // Keep trying until the max retries is reached. -retryCmd: - for remaining := actionDefaults.MaxRetries + 1; remaining > 0; remaining-- { - - // Perform the action run. - tryCmd := func(ctx context.Context) error { - // Try running the command and continue the retry loop if it fails. - if out, err = actionRun(ctx, actionDefaults, cmd, actionDefaults.Shell, spinner); err != nil { - return err - } - - out = strings.TrimSpace(out) - - // If an output variable is defined, set it. - for _, v := range action.SetVariables { - variableConfig.SetVariable(v.Name, out, v.Sensitive, v.AutoIndent, v.Type) - if err := variableConfig.CheckVariablePattern(v.Name, v.Pattern); err != nil { - message.WarnErr(err, err.Error()) - return err - } - } - - // If the action has a wait, change the spinner message to reflect that on success. - if action.Wait != nil { - spinner.Successf("Wait for \"%s\" succeeded", cmdEscaped) - } else { - spinner.Successf("Completed \"%s\"", cmdEscaped) - } - - // If the command ran successfully, continue to the next action. - return nil - } - - // If no timeout is set, run the command and return or continue retrying. - if actionDefaults.MaxTotalSeconds < 1 { - spinner.Updatef("Waiting for \"%s\" (no timeout)", cmdEscaped) - if err := tryCmd(context.TODO()); err != nil { - continue retryCmd - } - - return nil + if vc != nil { + for k, v := range vc.GetAllTemplates() { + // Remove # from env variable name. + k = strings.ReplaceAll(k, "#", "") + // Make terraform variables available to the action as TF_VAR_lowercase_name. + k1 := strings.ReplaceAll(strings.ToLower(k), "zarf_var", "TF_VAR") + cmdEnv = append(cmdEnv, fmt.Sprintf("%s=%s", k, v.Value)) + cmdEnv = append(cmdEnv, fmt.Sprintf("%s=%s", k1, v.Value)) } + } - // Run the command on repeat until success or timeout. - spinner.Updatef("Waiting for \"%s\" (timeout: %ds)", cmdEscaped, actionDefaults.MaxTotalSeconds) - select { - // On timeout break the loop to abort. - case <-timeout: - break retryCmd + cmd := exec.CommandContext(ctx, cmdargs[0], cmdargs[1:]...) + absPath, err := filepath.Abs(action.Dir) + if err != nil { + return err + } + cmd.Dir = absPath + cmd.Env = cmdEnv - // Otherwise, try running the command. - default: - ctx, cancel = context.WithTimeout(context.Background(), duration) - defer cancel() - if err := tryCmd(ctx); err != nil { - continue retryCmd - } + var stdoutBuf bytes.Buffer + var stderrBuf bytes.Buffer + stdoutMulti := io.MultiWriter(os.Stdout, &stdoutBuf) + stderrMulti := io.MultiWriter(os.Stderr, &stderrBuf) + cmd.Stdout = stdoutMulti + cmd.Stderr = stderrMulti - return nil - } + err = cmd.Run() + if err != nil { + return fmt.Errorf("%s: %w", stderrBuf.String(), err) } - select { - case <-timeout: - // If we reached this point, the timeout was reached or command failed with no retries. - if actionDefaults.MaxTotalSeconds < 1 { - return fmt.Errorf("command %q failed after %d retries", cmdEscaped, actionDefaults.MaxRetries) - } else { - return fmt.Errorf("command %q timed out after %d seconds", cmdEscaped, actionDefaults.MaxTotalSeconds) + for _, v := range action.SetVariables { + vc.SetVariable(v.Name, strings.TrimSpace(stdoutBuf.String()), v.Sensitive, v.AutoIndent, v.Type) + if err := vc.CheckVariablePattern(v.Name, v.Pattern); err != nil { + return err } - default: - // If we reached this point, the retry limit was reached. - return fmt.Errorf("command %q failed after %d retries", cmdEscaped, actionDefaults.MaxRetries) } + + return nil } // convertWaitToCmd will return the wait command if it exists, otherwise it will return the original command. -func convertWaitToCmd(wait types.ZarfComponentActionWait, timeout *int) (string, error) { - // Build the timeout string. - timeoutString := fmt.Sprintf("--timeout %ds", *timeout) - - // If the action has a wait, build a cmd from that instead. +func convertWaitToCmd(wait *types.ZarfComponentActionWait) (string, error) { cluster := wait.Cluster + network := wait.Network + + if cluster == nil && network == nil { + return "", errors.New("wait action is missing a cluster or network") + } + if cluster != nil { ns := cluster.Namespace if ns != "" { ns = fmt.Sprintf("-n %s", ns) } - - // Build a call to the zarf tools wait-for command. - return fmt.Sprintf("./zarf tools wait-for %s %s %s %s %s", - cluster.Kind, cluster.Identifier, cluster.Condition, ns, timeoutString), nil + return fmt.Sprintf("./zarf tools wait-for %s %s %s %s", + cluster.Kind, cluster.Identifier, cluster.Condition, ns), nil } - network := wait.Network if network != nil { - // Make sure the protocol is lower case. network.Protocol = strings.ToLower(network.Protocol) - - // If the protocol is http and no code is set, default to 200. if strings.HasPrefix(network.Protocol, "http") && network.Code == 0 { network.Code = 200 } - - // Build a call to the zarf tools wait-for command. - return fmt.Sprintf("./zarf tools wait-for %s %s %d %s", - network.Protocol, network.Address, network.Code, timeoutString), nil - } - - return "", fmt.Errorf("wait action is missing a cluster or network") -} - -// Perform some basic string mutations to make commands more useful. -func actionCmdMutation(cmd string, shellPref exec.Shell) (string, error) { - zarfCommand, err := utils.GetFinalExecutableCommand() - if err != nil { - return cmd, err - } - - // Try to patch the zarf binary path in case the name isn't exactly "./zarf". - cmd = strings.ReplaceAll(cmd, "./zarf ", zarfCommand+" ") - - // Make commands 'more' compatible with Windows OS PowerShell - if runtime.GOOS == "windows" && (exec.IsPowershell(shellPref.Windows) || shellPref.Windows == "") { - // Replace "touch" with "New-Item" on Windows as it's a common command, but not POSIX so not aliased by M$. - // See https://mathieubuisson.github.io/powershell-linux-bash/ & - // http://web.cs.ucla.edu/~miryung/teaching/EE461L-Spring2012/labs/posix.html for more details. - cmd = regexp.MustCompile(`^touch `).ReplaceAllString(cmd, `New-Item `) - - // Convert any ${ZARF_VAR_*} or $ZARF_VAR_* to ${env:ZARF_VAR_*} or $env:ZARF_VAR_* respectively (also TF_VAR_*). - // https://regex101.com/r/xk1rkw/1 - envVarRegex := regexp.MustCompile(`(?P\${?(?P(ZARF|TF)_VAR_([a-zA-Z0-9_-])+)}?)`) - get, err := helpers.MatchRegex(envVarRegex, cmd) - if err == nil { - newCmd := strings.ReplaceAll(cmd, get("envIndicator"), fmt.Sprintf("$Env:%s", get("varName"))) - message.Debugf("Converted command \"%s\" to \"%s\" t", cmd, newCmd) - cmd = newCmd - } - } - - return cmd, nil -} - -// Merge the ActionSet defaults with the action config. -func actionGetCfg(cfg types.ZarfComponentActionDefaults, a types.ZarfComponentAction, vars map[string]*variables.TextTemplate) types.ZarfComponentActionDefaults { - if a.Mute != nil { - cfg.Mute = *a.Mute - } - - // Default is no timeout, but add a timeout if one is provided. - if a.MaxTotalSeconds != nil { - cfg.MaxTotalSeconds = *a.MaxTotalSeconds - } - - if a.MaxRetries != nil { - cfg.MaxRetries = *a.MaxRetries - } - - if a.Dir != nil { - cfg.Dir = *a.Dir - } - - if len(a.Env) > 0 { - cfg.Env = append(cfg.Env, a.Env...) - } - - if a.Shell != nil { - cfg.Shell = *a.Shell - } - - // Add variables to the environment. - for k, v := range vars { - // Remove # from env variable name. - k = strings.ReplaceAll(k, "#", "") - // Make terraform variables available to the action as TF_VAR_lowercase_name. - k1 := strings.ReplaceAll(strings.ToLower(k), "zarf_var", "TF_VAR") - cfg.Env = append(cfg.Env, fmt.Sprintf("%s=%s", k, v.Value)) - cfg.Env = append(cfg.Env, fmt.Sprintf("%s=%s", k1, v.Value)) - } - - return cfg -} - -func actionRun(ctx context.Context, cfg types.ZarfComponentActionDefaults, cmd string, shellPref exec.Shell, spinner *message.Spinner) (string, error) { - shell, shellArgs := exec.GetOSShell(shellPref) - - message.Debugf("Running command in %s: %s", shell, cmd) - - execCfg := exec.Config{ - Env: cfg.Env, - Dir: cfg.Dir, - } - - if !cfg.Mute { - execCfg.Stdout = spinner - execCfg.Stderr = spinner - } - - out, errOut, err := exec.CmdWithContext(ctx, execCfg, shell, append(shellArgs, cmd)...) - // Dump final complete output (respect mute to prevent sensitive values from hitting the logs). - if !cfg.Mute { - message.Debug(cmd, out, errOut) + return fmt.Sprintf("./zarf tools wait-for %s %s %d", + network.Protocol, network.Address, network.Code), nil } - return out, err + return "", nil } diff --git a/src/pkg/packager/composer/list_test.go b/src/pkg/packager/composer/list_test.go index 0f96e0beb0..3ea45bc151 100644 --- a/src/pkg/packager/composer/list_test.go +++ b/src/pkg/packager/composer/list_test.go @@ -62,10 +62,6 @@ func TestCompose(t *testing.T) { secondDirectory := "world" finalDirectory := filepath.Join(firstDirectory, secondDirectory) - finalDirectoryActionDefault := filepath.Join(firstDirectory, secondDirectory, "today-dc") - secondDirectoryActionDefault := filepath.Join(firstDirectory, "world-dc") - firstDirectoryActionDefault := "hello-dc" - tests := []struct { name string ic *ImportChain @@ -137,86 +133,7 @@ func TestCompose(t *testing.T) { {Source: fmt.Sprintf("%s%sworld", firstDirectory, string(os.PathSeparator))}, {Source: "hello"}, }, - Actions: types.ZarfComponentActions{ - // OnCreate actions should be appended with corrected directories that properly handle default directories - OnCreate: types.ZarfComponentActionSet{ - Defaults: types.ZarfComponentActionDefaults{ - Dir: "hello-dc", - }, - Before: []types.ZarfComponentAction{ - {Cmd: "today-bc", Dir: &finalDirectoryActionDefault}, - {Cmd: "world-bc", Dir: &secondDirectoryActionDefault}, - {Cmd: "hello-bc", Dir: &firstDirectoryActionDefault}, - }, - After: []types.ZarfComponentAction{ - {Cmd: "today-ac", Dir: &finalDirectoryActionDefault}, - {Cmd: "world-ac", Dir: &secondDirectoryActionDefault}, - {Cmd: "hello-ac", Dir: &firstDirectoryActionDefault}, - }, - OnSuccess: []types.ZarfComponentAction{ - {Cmd: "today-sc", Dir: &finalDirectoryActionDefault}, - {Cmd: "world-sc", Dir: &secondDirectoryActionDefault}, - {Cmd: "hello-sc", Dir: &firstDirectoryActionDefault}, - }, - OnFailure: []types.ZarfComponentAction{ - {Cmd: "today-fc", Dir: &finalDirectoryActionDefault}, - {Cmd: "world-fc", Dir: &secondDirectoryActionDefault}, - {Cmd: "hello-fc", Dir: &firstDirectoryActionDefault}, - }, - }, - // OnDeploy actions should be appended without corrected directories - OnDeploy: types.ZarfComponentActionSet{ - Defaults: types.ZarfComponentActionDefaults{ - Dir: "hello-dd", - }, - Before: []types.ZarfComponentAction{ - {Cmd: "today-bd"}, - {Cmd: "world-bd"}, - {Cmd: "hello-bd"}, - }, - After: []types.ZarfComponentAction{ - {Cmd: "today-ad"}, - {Cmd: "world-ad"}, - {Cmd: "hello-ad"}, - }, - OnSuccess: []types.ZarfComponentAction{ - {Cmd: "today-sd"}, - {Cmd: "world-sd"}, - {Cmd: "hello-sd"}, - }, - OnFailure: []types.ZarfComponentAction{ - {Cmd: "today-fd"}, - {Cmd: "world-fd"}, - {Cmd: "hello-fd"}, - }, - }, - // OnRemove actions should be appended without corrected directories - OnRemove: types.ZarfComponentActionSet{ - Defaults: types.ZarfComponentActionDefaults{ - Dir: "hello-dr", - }, - Before: []types.ZarfComponentAction{ - {Cmd: "today-br"}, - {Cmd: "world-br"}, - {Cmd: "hello-br"}, - }, - After: []types.ZarfComponentAction{ - {Cmd: "today-ar"}, - {Cmd: "world-ar"}, - {Cmd: "hello-ar"}, - }, - OnSuccess: []types.ZarfComponentAction{ - {Cmd: "today-sr"}, - {Cmd: "world-sr"}, - {Cmd: "hello-sr"}, - }, - OnFailure: []types.ZarfComponentAction{ - {Cmd: "today-fr"}, - {Cmd: "world-fr"}, - {Cmd: "hello-fr"}, - }, - }, - }, + // Extensions should be appended with corrected directories Extensions: extensions.ZarfComponentExtensions{ BigBang: &extensions.BigBang{ @@ -476,59 +393,6 @@ func createDummyComponent(t *testing.T, name, importDir, subName string) types.Z Source: name, }, }, - Actions: types.ZarfComponentActions{ - OnCreate: types.ZarfComponentActionSet{ - Defaults: types.ZarfComponentActionDefaults{ - Dir: name + "-dc", - }, - Before: []types.ZarfComponentAction{ - {Cmd: name + "-bc"}, - }, - After: []types.ZarfComponentAction{ - {Cmd: name + "-ac"}, - }, - OnSuccess: []types.ZarfComponentAction{ - {Cmd: name + "-sc"}, - }, - OnFailure: []types.ZarfComponentAction{ - {Cmd: name + "-fc"}, - }, - }, - OnDeploy: types.ZarfComponentActionSet{ - Defaults: types.ZarfComponentActionDefaults{ - Dir: name + "-dd", - }, - Before: []types.ZarfComponentAction{ - {Cmd: name + "-bd"}, - }, - After: []types.ZarfComponentAction{ - {Cmd: name + "-ad"}, - }, - OnSuccess: []types.ZarfComponentAction{ - {Cmd: name + "-sd"}, - }, - OnFailure: []types.ZarfComponentAction{ - {Cmd: name + "-fd"}, - }, - }, - OnRemove: types.ZarfComponentActionSet{ - Defaults: types.ZarfComponentActionDefaults{ - Dir: name + "-dr", - }, - Before: []types.ZarfComponentAction{ - {Cmd: name + "-br"}, - }, - After: []types.ZarfComponentAction{ - {Cmd: name + "-ar"}, - }, - OnSuccess: []types.ZarfComponentAction{ - {Cmd: name + "-sr"}, - }, - OnFailure: []types.ZarfComponentAction{ - {Cmd: name + "-fr"}, - }, - }, - }, Extensions: extensions.ZarfComponentExtensions{ BigBang: &extensions.BigBang{ ValuesFiles: []string{ diff --git a/src/pkg/packager/composer/override.go b/src/pkg/packager/composer/override.go index 4a07a1d936..674538dd47 100644 --- a/src/pkg/packager/composer/override.go +++ b/src/pkg/packager/composer/override.go @@ -55,26 +55,7 @@ func overrideDeprecated(c *types.ZarfComponent, override types.ZarfComponent) { } func overrideActions(c *types.ZarfComponent, override types.ZarfComponent) { - // Merge create actions. - c.Actions.OnCreate.Defaults = override.Actions.OnCreate.Defaults - c.Actions.OnCreate.Before = append(c.Actions.OnCreate.Before, override.Actions.OnCreate.Before...) - c.Actions.OnCreate.After = append(c.Actions.OnCreate.After, override.Actions.OnCreate.After...) - c.Actions.OnCreate.OnFailure = append(c.Actions.OnCreate.OnFailure, override.Actions.OnCreate.OnFailure...) - c.Actions.OnCreate.OnSuccess = append(c.Actions.OnCreate.OnSuccess, override.Actions.OnCreate.OnSuccess...) - - // Merge deploy actions. - c.Actions.OnDeploy.Defaults = override.Actions.OnDeploy.Defaults - c.Actions.OnDeploy.Before = append(c.Actions.OnDeploy.Before, override.Actions.OnDeploy.Before...) - c.Actions.OnDeploy.After = append(c.Actions.OnDeploy.After, override.Actions.OnDeploy.After...) - c.Actions.OnDeploy.OnFailure = append(c.Actions.OnDeploy.OnFailure, override.Actions.OnDeploy.OnFailure...) - c.Actions.OnDeploy.OnSuccess = append(c.Actions.OnDeploy.OnSuccess, override.Actions.OnDeploy.OnSuccess...) - - // Merge remove actions. - c.Actions.OnRemove.Defaults = override.Actions.OnRemove.Defaults - c.Actions.OnRemove.Before = append(c.Actions.OnRemove.Before, override.Actions.OnRemove.Before...) - c.Actions.OnRemove.After = append(c.Actions.OnRemove.After, override.Actions.OnRemove.After...) - c.Actions.OnRemove.OnFailure = append(c.Actions.OnRemove.OnFailure, override.Actions.OnRemove.OnFailure...) - c.Actions.OnRemove.OnSuccess = append(c.Actions.OnRemove.OnSuccess, override.Actions.OnRemove.OnSuccess...) + c.Actions = append(c.Actions, override.Actions...) } func overrideResources(c *types.ZarfComponent, override types.ZarfComponent) { diff --git a/src/pkg/packager/composer/pathfixer.go b/src/pkg/packager/composer/pathfixer.go index fac110c47c..97b79c1067 100644 --- a/src/pkg/packager/composer/pathfixer.go +++ b/src/pkg/packager/composer/pathfixer.go @@ -57,29 +57,9 @@ func fixPaths(child *types.ZarfComponent, relativeToHead string) { child.DataInjections[dataInjectionsIdx].Source = composed } - defaultDir := child.Actions.OnCreate.Defaults.Dir - child.Actions.OnCreate.Before = fixActionPaths(child.Actions.OnCreate.Before, defaultDir, relativeToHead) - child.Actions.OnCreate.After = fixActionPaths(child.Actions.OnCreate.After, defaultDir, relativeToHead) - child.Actions.OnCreate.OnFailure = fixActionPaths(child.Actions.OnCreate.OnFailure, defaultDir, relativeToHead) - child.Actions.OnCreate.OnSuccess = fixActionPaths(child.Actions.OnCreate.OnSuccess, defaultDir, relativeToHead) - // deprecated if child.DeprecatedCosignKeyPath != "" { composed := makePathRelativeTo(child.DeprecatedCosignKeyPath, relativeToHead) child.DeprecatedCosignKeyPath = composed } } - -// fixActionPaths takes a slice of actions and mutates the Dir to be relative to the head node -func fixActionPaths(actions []types.ZarfComponentAction, defaultDir, relativeToHead string) []types.ZarfComponentAction { - for actionIdx, action := range actions { - var composed string - if action.Dir != nil { - composed = makePathRelativeTo(*action.Dir, relativeToHead) - } else { - composed = makePathRelativeTo(defaultDir, relativeToHead) - } - actions[actionIdx].Dir = &composed - } - return actions -} diff --git a/src/pkg/packager/creator/normal.go b/src/pkg/packager/creator/normal.go index 027df385fa..2b4c47d65c 100644 --- a/src/pkg/packager/creator/normal.go +++ b/src/pkg/packager/creator/normal.go @@ -134,24 +134,10 @@ func (pc *PackageCreator) Assemble(ctx context.Context, dst *layout.PackagePaths componentSBOMs := map[string]*layout.ComponentSBOM{} for _, component := range components { - onCreate := component.Actions.OnCreate - - onFailure := func() { - if err := actions.Run(onCreate.Defaults, onCreate.OnFailure, nil); err != nil { - message.Debugf("unable to run component failure action: %s", err.Error()) - } - } - - if err := pc.addComponent(component, dst); err != nil { - onFailure() + if err := pc.addComponent(ctx, component, dst); err != nil { return fmt.Errorf("unable to add component %q: %w", component.Name, err) } - if err := actions.Run(onCreate.Defaults, onCreate.OnSuccess, nil); err != nil { - onFailure() - return fmt.Errorf("unable to run component success action: %w", err) - } - if !skipSBOMFlagUsed { componentSBOM, err := pc.getFilesToSBOM(component, dst) if err != nil { @@ -348,7 +334,7 @@ func (pc *PackageCreator) processExtensions(components []types.ZarfComponent, la return processedComponents, nil } -func (pc *PackageCreator) addComponent(component types.ZarfComponent, dst *layout.PackagePaths) error { +func (pc *PackageCreator) addComponent(ctx context.Context, component types.ZarfComponent, dst *layout.PackagePaths) error { message.HeaderInfof("📦 %s COMPONENT", strings.ToUpper(component.Name)) componentPaths, err := dst.Components.Create(component) @@ -356,9 +342,8 @@ func (pc *PackageCreator) addComponent(component types.ZarfComponent, dst *layou return err } - onCreate := component.Actions.OnCreate - if err := actions.Run(onCreate.Defaults, onCreate.Before, nil); err != nil { - return fmt.Errorf("unable to run component before action: %w", err) + if err := actions.Run(ctx, component.Actions, types.BeforeCreate, nil); err != nil { + return fmt.Errorf("unable to run %s action: %w", string(types.BeforeCreate), err) } // If any helm charts are defined, process them. @@ -521,8 +506,8 @@ func (pc *PackageCreator) addComponent(component types.ZarfComponent, dst *layou spinner.Success() } - if err := actions.Run(onCreate.Defaults, onCreate.After, nil); err != nil { - return fmt.Errorf("unable to run component after action: %w", err) + if err := actions.Run(ctx, component.Actions, types.AfterCreate, nil); err != nil { + return fmt.Errorf("unable to run %s action: %w", string(types.AfterCreate), err) } return nil diff --git a/src/pkg/packager/creator/skeleton.go b/src/pkg/packager/creator/skeleton.go index e0e86126b4..6c42fcb943 100644 --- a/src/pkg/packager/creator/skeleton.go +++ b/src/pkg/packager/creator/skeleton.go @@ -165,24 +165,8 @@ func (sc *SkeletonCreator) addComponent(component types.ZarfComponent, dst *layo updatedComponent.DeprecatedCosignKeyPath = "cosign.pub" } - // TODO: (@WSTARR) Shim the skeleton component's create action dirs to be empty. This prevents actions from failing by cd'ing into directories that will be flattened. - updatedComponent.Actions.OnCreate.Defaults.Dir = "" - - resetActions := func(actions []types.ZarfComponentAction) []types.ZarfComponentAction { - for idx := range actions { - actions[idx].Dir = nil - } - return actions - } - - updatedComponent.Actions.OnCreate.Before = resetActions(component.Actions.OnCreate.Before) - updatedComponent.Actions.OnCreate.After = resetActions(component.Actions.OnCreate.After) - updatedComponent.Actions.OnCreate.OnSuccess = resetActions(component.Actions.OnCreate.OnSuccess) - updatedComponent.Actions.OnCreate.OnFailure = resetActions(component.Actions.OnCreate.OnFailure) - // If any helm charts are defined, process them. for chartIdx, chart := range component.Charts { - if chart.LocalPath != "" { rel := filepath.Join(layout.ChartsDir, fmt.Sprintf("%s-%d", chart.Name, chartIdx)) dst := filepath.Join(componentPaths.Base, rel) diff --git a/src/pkg/packager/deploy.go b/src/pkg/packager/deploy.go index 8c0eb6b7a4..e0f87fd28e 100644 --- a/src/pkg/packager/deploy.go +++ b/src/pkg/packager/deploy.go @@ -183,17 +183,7 @@ func (p *Packager) deployComponents(ctx context.Context) (deployedComponents []t charts, deployErr = p.deployComponent(ctx, component, false /* keep img checksum */, false /* always push images */) } - onDeploy := component.Actions.OnDeploy - - onFailure := func() { - if err := actions.Run(onDeploy.Defaults, onDeploy.OnFailure, p.variableConfig); err != nil { - message.Debugf("unable to run component failure action: %s", err.Error()) - } - } - if deployErr != nil { - onFailure() - // Update the package secret to indicate that we failed to deploy this component deployedComponents[idx].Status = types.ComponentStatusFailed if p.isConnectedToCluster() { @@ -213,11 +203,6 @@ func (p *Packager) deployComponents(ctx context.Context) (deployedComponents []t message.Debugf("Unable to record package deployment for component %q: this will affect features like `zarf package remove`: %s", component.Name, err.Error()) } } - - if err := actions.Run(onDeploy.Defaults, onDeploy.OnSuccess, p.variableConfig); err != nil { - onFailure() - return deployedComponents, fmt.Errorf("unable to run component success action: %w", err) - } } return deployedComponents, nil @@ -291,8 +276,6 @@ func (p *Packager) deployComponent(ctx context.Context, component types.ZarfComp hasDataInjections := len(component.DataInjections) > 0 hasFiles := len(component.Files) > 0 - onDeploy := component.Actions.OnDeploy - if component.RequiresCluster() { // Setup the state in the config if p.state == nil { @@ -317,8 +300,8 @@ func (p *Packager) deployComponent(ctx context.Context, component types.ZarfComp return charts, err } - if err = actions.Run(onDeploy.Defaults, onDeploy.Before, p.variableConfig); err != nil { - return charts, fmt.Errorf("unable to run component before action: %w", err) + if err := actions.Run(ctx, component.Actions, types.BeforeDeploy, p.variableConfig); err != nil { + return nil, fmt.Errorf("unable to run %s action: %w", string(types.BeforeDeploy), err) } if hasFiles { @@ -355,8 +338,8 @@ func (p *Packager) deployComponent(ctx context.Context, component types.ZarfComp } } - if err = actions.Run(onDeploy.Defaults, onDeploy.After, p.variableConfig); err != nil { - return charts, fmt.Errorf("unable to run component after action: %w", err) + if err := actions.Run(ctx, component.Actions, types.AfterDeploy, p.variableConfig); err != nil { + return nil, fmt.Errorf("unable to run %s action: %w", string(types.AfterDeploy), err) } return charts, nil diff --git a/src/pkg/packager/deprecated/pluralize-set-variable.go b/src/pkg/packager/deprecated/pluralize-set-variable.go index c3dc13e06c..72e3a06f24 100644 --- a/src/pkg/packager/deprecated/pluralize-set-variable.go +++ b/src/pkg/packager/deprecated/pluralize-set-variable.go @@ -13,75 +13,26 @@ import ( func migrateSetVariableToSetVariables(c types.ZarfComponent) (types.ZarfComponent, string) { hasSetVariable := false - - migrate := func(actions []types.ZarfComponentAction) []types.ZarfComponentAction { - for i := range actions { - if actions[i].DeprecatedSetVariable != "" && len(actions[i].SetVariables) < 1 { - hasSetVariable = true - actions[i].SetVariables = []variables.Variable{ - { - Name: actions[i].DeprecatedSetVariable, - Sensitive: false, - }, - } + for i := range c.Actions { + if c.Actions[i].DeprecatedSetVariable != "" && len(c.Actions[i].SetVariables) < 1 { + hasSetVariable = true + c.Actions[i].SetVariables = []variables.Variable{ + { + Name: c.Actions[i].DeprecatedSetVariable, + Sensitive: false, + }, } } - - return actions } - - // Migrate OnCreate SetVariables - c.Actions.OnCreate.After = migrate(c.Actions.OnCreate.After) - c.Actions.OnCreate.Before = migrate(c.Actions.OnCreate.Before) - c.Actions.OnCreate.OnSuccess = migrate(c.Actions.OnCreate.OnSuccess) - c.Actions.OnCreate.OnFailure = migrate(c.Actions.OnCreate.OnFailure) - - // Migrate OnDeploy SetVariables - c.Actions.OnDeploy.After = migrate(c.Actions.OnDeploy.After) - c.Actions.OnDeploy.Before = migrate(c.Actions.OnDeploy.Before) - c.Actions.OnDeploy.OnSuccess = migrate(c.Actions.OnDeploy.OnSuccess) - c.Actions.OnDeploy.OnFailure = migrate(c.Actions.OnDeploy.OnFailure) - - // Migrate OnRemove SetVariables - c.Actions.OnRemove.After = migrate(c.Actions.OnRemove.After) - c.Actions.OnRemove.Before = migrate(c.Actions.OnRemove.Before) - c.Actions.OnRemove.OnSuccess = migrate(c.Actions.OnRemove.OnSuccess) - c.Actions.OnRemove.OnFailure = migrate(c.Actions.OnRemove.OnFailure) - - // Leave deprecated setVariable in place, but warn users if hasSetVariable { - return c, fmt.Sprintf("Component '%s' is using setVariable in actions which will be removed in Zarf v1.0.0. Please migrate to the list form of setVariables.", c.Name) + return c, fmt.Sprintf("Component %q is using setVariable in actions which will be removed in Zarf v1.0.0. Please migrate to the list form of setVariables.", c.Name) } - return c, "" } func clearSetVariables(c types.ZarfComponent) types.ZarfComponent { - clear := func(actions []types.ZarfComponentAction) []types.ZarfComponentAction { - for i := range actions { - actions[i].DeprecatedSetVariable = "" - } - - return actions + for i := range c.Actions { + c.Actions[i].DeprecatedSetVariable = "" } - - // Clear OnCreate SetVariables - c.Actions.OnCreate.After = clear(c.Actions.OnCreate.After) - c.Actions.OnCreate.Before = clear(c.Actions.OnCreate.Before) - c.Actions.OnCreate.OnSuccess = clear(c.Actions.OnCreate.OnSuccess) - c.Actions.OnCreate.OnFailure = clear(c.Actions.OnCreate.OnFailure) - - // Clear OnDeploy SetVariables - c.Actions.OnDeploy.After = clear(c.Actions.OnDeploy.After) - c.Actions.OnDeploy.Before = clear(c.Actions.OnDeploy.Before) - c.Actions.OnDeploy.OnSuccess = clear(c.Actions.OnDeploy.OnSuccess) - c.Actions.OnDeploy.OnFailure = clear(c.Actions.OnDeploy.OnFailure) - - // Clear OnRemove SetVariables - c.Actions.OnRemove.After = clear(c.Actions.OnRemove.After) - c.Actions.OnRemove.Before = clear(c.Actions.OnRemove.Before) - c.Actions.OnRemove.OnSuccess = clear(c.Actions.OnRemove.OnSuccess) - c.Actions.OnRemove.OnFailure = clear(c.Actions.OnRemove.OnFailure) - return c } diff --git a/src/pkg/packager/deprecated/scripts-to-actions.go b/src/pkg/packager/deprecated/scripts-to-actions.go index 2040e7eb90..e9f68cf829 100644 --- a/src/pkg/packager/deprecated/scripts-to-actions.go +++ b/src/pkg/packager/deprecated/scripts-to-actions.go @@ -6,7 +6,6 @@ package deprecated import ( "fmt" - "math" "github.com/defenseunicorns/zarf/src/types" ) @@ -21,49 +20,39 @@ import ( func migrateScriptsToActions(c types.ZarfComponent) (types.ZarfComponent, string) { var hasScripts bool - // Convert a script configs to action defaults. - defaults := types.ZarfComponentActionDefaults{ - // ShowOutput (default false) -> Mute (default false) - Mute: !c.DeprecatedScripts.ShowOutput, - // TimeoutSeconds -> MaxSeconds - MaxTotalSeconds: c.DeprecatedScripts.TimeoutSeconds, - } - - // Retry is now an integer vs a boolean (implicit infinite retries), so set to an absurdly high number - if c.DeprecatedScripts.Retry { - defaults.MaxRetries = math.MaxInt - } - - // Scripts.Prepare -> Actions.Create.Before if len(c.DeprecatedScripts.Prepare) > 0 { hasScripts = true - c.Actions.OnCreate.Defaults = defaults for _, s := range c.DeprecatedScripts.Prepare { - c.Actions.OnCreate.Before = append(c.Actions.OnCreate.Before, types.ZarfComponentAction{Cmd: s}) + c.Actions = append(c.Actions, types.ZarfComponentAction{ + Cmd: s, + When: types.BeforeCreate, + }) } } - // Scripts.Before -> Actions.Deploy.Before if len(c.DeprecatedScripts.Before) > 0 { hasScripts = true - c.Actions.OnDeploy.Defaults = defaults for _, s := range c.DeprecatedScripts.Before { - c.Actions.OnDeploy.Before = append(c.Actions.OnDeploy.Before, types.ZarfComponentAction{Cmd: s}) + c.Actions = append(c.Actions, types.ZarfComponentAction{ + Cmd: s, + When: types.BeforeDeploy, + }) } } - // Scripts.After -> Actions.Deploy.After if len(c.DeprecatedScripts.After) > 0 { hasScripts = true - c.Actions.OnDeploy.Defaults = defaults for _, s := range c.DeprecatedScripts.After { - c.Actions.OnDeploy.After = append(c.Actions.OnDeploy.After, types.ZarfComponentAction{Cmd: s}) + c.Actions = append(c.Actions, types.ZarfComponentAction{ + Cmd: s, + When: types.AfterDeploy, + }) } } // Leave deprecated scripts in place, but warn users if hasScripts { - return c, fmt.Sprintf("Component '%s' is using scripts which will be removed in Zarf v1.0.0. Please migrate to actions.", c.Name) + return c, fmt.Sprintf("Component %q is using scripts which will be removed in Zarf v1.0.0. Please migrate to actions.", c.Name) } return c, "" diff --git a/src/pkg/packager/lint/lint_test.go b/src/pkg/packager/lint/lint_test.go index d20257b23a..5905930d95 100644 --- a/src/pkg/packager/lint/lint_test.go +++ b/src/pkg/packager/lint/lint_test.go @@ -75,22 +75,16 @@ func TestZarfSchema(t *testing.T) { }, { Name: "actions", - Actions: types.ZarfComponentActions{ - OnCreate: types.ZarfComponentActionSet{ - Before: []types.ZarfComponentAction{ - { - Cmd: "echo 'invalid setVariable'", - SetVariables: []variables.Variable{{Name: "not_uppercase"}}, - }, - }, + Actions: []types.ZarfComponentAction{ + { + When: types.BeforeCreate, + Cmd: "echo 'invalid setVariable'", + SetVariables: []variables.Variable{{Name: "not_uppercase"}}, }, - OnRemove: types.ZarfComponentActionSet{ - OnSuccess: []types.ZarfComponentAction{ - { - Cmd: "echo 'invalid setVariable'", - SetVariables: []variables.Variable{{Name: "not_uppercase"}}, - }, - }, + { + When: types.SuccessfulRemove, + Cmd: "echo 'invalid setVariable'", + SetVariables: []variables.Variable{{Name: "not_uppercase"}}, }, }, }, @@ -111,8 +105,8 @@ func TestZarfSchema(t *testing.T) { "variables.0.name: Does not match pattern '^[A-Z0-9_]+$'", "constants.0.name: Does not match pattern '^[A-Z0-9_]+$'", "components.0.only.localOS: components.0.only.localOS must be one of the following: \"linux\", \"darwin\", \"windows\"", - "components.1.actions.onCreate.before.0.setVariables.0.name: Does not match pattern '^[A-Z0-9_]+$'", - "components.1.actions.onRemove.onSuccess.0.setVariables.0.name: Does not match pattern '^[A-Z0-9_]+$'", + "components.1.actions.0.setVariables.0.name: Does not match pattern '^[A-Z0-9_]+$'", + "components.1.actions.1.setVariables.0.name: Does not match pattern '^[A-Z0-9_]+$'", "components.0.import.path: Must not validate the schema (not)", "components.0.import.url: Must not validate the schema (not)", }, diff --git a/src/pkg/packager/remove.go b/src/pkg/packager/remove.go index be34b6dda7..297b32e6f2 100644 --- a/src/pkg/packager/remove.go +++ b/src/pkg/packager/remove.go @@ -167,16 +167,8 @@ func (p *Packager) removeComponent(ctx context.Context, deployedPackage *types.D return t.Name == deployedComponent.Name }) - onRemove := c.Actions.OnRemove - onFailure := func() { - if err := actions.Run(onRemove.Defaults, onRemove.OnFailure, nil); err != nil { - message.Debugf("Unable to run the failure action: %s", err) - } - } - - if err := actions.Run(onRemove.Defaults, onRemove.Before, nil); err != nil { - onFailure() - return nil, fmt.Errorf("unable to run the before action for component (%s): %w", c.Name, err) + if err := actions.Run(ctx, c.Actions, types.BeforeRemove, nil); err != nil { + return nil, fmt.Errorf("unable to run %s action: %w", string(types.BeforeRemove), err) } for _, chart := range helpers.Reverse(deployedComponent.InstalledCharts) { @@ -185,7 +177,6 @@ func (p *Packager) removeComponent(ctx context.Context, deployedPackage *types.D helmCfg := helm.NewClusterOnly(p.cfg, p.variableConfig, p.state, p.cluster) if err := helmCfg.RemoveChart(chart.Namespace, chart.ChartName, spinner); err != nil { if !errors.Is(err, driver.ErrReleaseNotFound) { - onFailure() return deployedPackage, fmt.Errorf("unable to uninstall the helm chart %s in the namespace %s: %w", chart.ChartName, chart.Namespace, err) } @@ -206,14 +197,8 @@ func (p *Packager) removeComponent(ctx context.Context, deployedPackage *types.D } } - if err := actions.Run(onRemove.Defaults, onRemove.After, nil); err != nil { - onFailure() - return deployedPackage, fmt.Errorf("unable to run the after action: %w", err) - } - - if err := actions.Run(onRemove.Defaults, onRemove.OnSuccess, nil); err != nil { - onFailure() - return deployedPackage, fmt.Errorf("unable to run the success action: %w", err) + if err := actions.Run(ctx, c.Actions, types.AfterRemove, nil); err != nil { + return nil, fmt.Errorf("unable to run %s action: %w", string(types.AfterRemove), err) } // Remove the component we just removed from the array diff --git a/src/test/e2e/02_component_actions_test.go b/src/test/e2e/02_component_actions_test.go index b709c0c267..333b17f35b 100644 --- a/src/test/e2e/02_component_actions_test.go +++ b/src/test/e2e/02_component_actions_test.go @@ -26,15 +26,12 @@ func TestComponentActions(t *testing.T) { } allArtifacts := append(deployArtifacts, createArtifacts...) - e2e.CleanFiles(allArtifacts...) - defer e2e.CleanFiles(allArtifacts...) + t.Cleanup(func() { e2e.CleanFiles(allArtifacts...) }) /* Create */ // Try creating the package to test the onCreate actions. stdOut, stdErr, err := e2e.Zarf("package", "create", "examples/component-actions", "--confirm") require.NoError(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "Completed \"Create a test file\"") - require.Contains(t, stdErr, "Completed \"touch test-create-after.txt\"") require.Contains(t, stdErr, "multiline!") require.Contains(t, stdErr, "updates!") require.Contains(t, stdErr, "realtime!") @@ -72,22 +69,13 @@ func TestComponentActions(t *testing.T) { } }) - t.Run("action on-deploy-with-timeout", func(t *testing.T) { - t.Parallel() - // Deploy the simple action that should fail the timeout. - stdOut, stdErr, err = e2e.Zarf("package", "deploy", path, "--components=on-deploy-with-timeout", "--confirm") - require.Error(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "after 1 second") - require.Contains(t, stdErr, "😭😭😭 this action failed because it took too long to run 😭😭😭") - }) - t.Run("action on-deploy-with-variable", func(t *testing.T) { t.Parallel() // Test using a Zarf Variable within the action stdOut, stdErr, err = e2e.Zarf("package", "deploy", path, "--components=on-deploy-with-variable", "--confirm") require.NoError(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "the dog says ruff") + require.Contains(t, stdOut, "the dog says ruff") }) @@ -96,11 +84,7 @@ func TestComponentActions(t *testing.T) { // Test using dynamic and multiple-variables stdOut, stdErr, err = e2e.Zarf("package", "deploy", path, "--components=on-deploy-with-dynamic-variable,on-deploy-with-multiple-variables", "--confirm") require.NoError(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "the cat says meow") - require.Contains(t, stdErr, "the dog says ruff") - require.Contains(t, stdErr, "the snake says hiss") - require.Contains(t, stdErr, "with a TF_VAR, the snake also says hiss") - + require.Equal(t, "meow\nthe cat says meow\nhiss\n", stdOut) }) t.Run("action on-deploy-with-env-var", func(t *testing.T) { diff --git a/src/test/e2e/03_deprecations_test.go b/src/test/e2e/03_deprecations_test.go index 9d1c96b0c9..557037b19b 100644 --- a/src/test/e2e/03_deprecations_test.go +++ b/src/test/e2e/03_deprecations_test.go @@ -33,9 +33,8 @@ func TestDeprecatedComponentScripts(t *testing.T) { stdOut, stdErr, err := e2e.Zarf("package", "create", testPackageDirPath, outputFlag, "--confirm") defer e2e.CleanFiles(testPackagePath) require.NoError(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "Component '1-test-deprecated-prepare-scripts' is using scripts") - require.Contains(t, stdErr, "Component '2-test-deprecated-deploy-scripts' is using scripts") - require.Contains(t, stdErr, "Component '3-test-deprecated-timeout-scripts' is using scripts") + require.Contains(t, stdErr, "Component \"1-test-deprecated-prepare-scripts\" is using scripts") + require.Contains(t, stdErr, "Component \"2-test-deprecated-deploy-scripts\" is using scripts") // Test for package create prepare artifact require.FileExists(t, prepareArtifact) @@ -53,10 +52,6 @@ func TestDeprecatedComponentScripts(t *testing.T) { for _, artifact := range deployArtifacts { require.FileExists(t, artifact) } - - // 3. Deploy the simple script that should fail the timeout - stdOut, stdErr, err = e2e.Zarf("package", "deploy", testPackagePath, "--confirm", "--components=3-test-deprecated-timeout-scripts") - require.Error(t, err, stdOut, stdErr) } // TestDeprecatedSetAndPackageVariables verifies that deprecated setVariables and PKG_VARs still able to be set. @@ -87,13 +82,13 @@ func TestDeprecatedSetAndPackageVariables(t *testing.T) { stdOut, stdErr, err = e2e.Zarf("package", "create", testPackageDirPath, outputFlag, "--confirm", "--set", "ECHO=Zarf-The-Axolotl") defer e2e.CleanFiles(testPackagePath) require.NoError(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "Component '1-test-deprecated-set-variable' is using setVariable") + require.Contains(t, stdErr, "Component \"1-test-deprecated-set-variable\" is using setVariable") require.Contains(t, stdErr, "deprecated syntax ###ZARF_PKG_VAR_ECHO###") // 1. Deploy the setVariable action that should pass and output the variable stdOut, stdErr, err = e2e.Zarf("package", "deploy", testPackagePath, "--confirm", "--components=1-test-deprecated-set-variable") require.NoError(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "Hello from Hello Kitteh") + require.Contains(t, stdOut, "Hello from Hello Kitteh") // 2. Deploy the setVariable action that should pass and output the variable stdOut, stdErr, err = e2e.Zarf("package", "deploy", testPackagePath, "--confirm", "--components=2-test-deprecated-pkg-var") diff --git a/src/test/e2e/04_create_templating_test.go b/src/test/e2e/04_create_templating_test.go index a77ba1abd6..c9dc9f67b7 100644 --- a/src/test/e2e/04_create_templating_test.go +++ b/src/test/e2e/04_create_templating_test.go @@ -50,9 +50,9 @@ func TestCreateTemplating(t *testing.T) { // Deploy the package and look for the variables in the output stdOut, stdErr, err = e2e.Zarf("package", "deploy", fileFoldersPkgName, "--set", "DOGGO=doggy", "--set", "KITTEH=meowza", "--set", "PANDA=pandemonium", "--confirm") require.NoError(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "A doggy barks!") - require.Contains(t, stdErr, " - meowza") - require.Contains(t, stdErr, "# Total pandemonium") + require.Contains(t, stdOut, "A doggy barks!") + require.Contains(t, stdOut, " - meowza") + require.Contains(t, stdOut, "# Total pandemonium") // Ensure that the `requirements.txt` files are discovered correctly require.FileExists(t, filepath.Join(sbomPath, "file-folders-templating-sbom", "compare.html")) diff --git a/src/test/e2e/09_component_compose_test.go b/src/test/e2e/09_component_compose_test.go index c5edaeb682..1f26894aed 100644 --- a/src/test/e2e/09_component_compose_test.go +++ b/src/test/e2e/09_component_compose_test.go @@ -63,9 +63,8 @@ func (suite *CompositionSuite) Test_0_ComposabilityExample() { suite.Contains(stdErr, ` - defenseunicorns/zarf-game:multi-tile-dark actions: - onDeploy: - before: - - cmd: ./zarf tools kubectl get -n dos-games deployment -o jsonpath={.items[0].metadata.creationTimestamp}`) + - when: BeforeDeploy + cmd: ./zarf tools kubectl get -n dos-games deployment -o jsonpath={.items[0].metadata.creationTimestamp}`) } func (suite *CompositionSuite) Test_1_FullComposability() { diff --git a/src/test/packages/03-deprecated-component-scripts/zarf.yaml b/src/test/packages/03-deprecated-component-scripts/zarf.yaml index 57a7c8f838..499cb573d1 100644 --- a/src/test/packages/03-deprecated-component-scripts/zarf.yaml +++ b/src/test/packages/03-deprecated-component-scripts/zarf.yaml @@ -18,10 +18,3 @@ components: - touch test-deprecated-deploy-before-hook.txt after: - touch test-deprecated-deploy-after-hook.txt - - # Test that script timeouts still get set - - name: 3-test-deprecated-timeout-scripts - scripts: - timeoutSeconds: 1 - before: - - sleep 5 diff --git a/src/test/packages/03-deprecated-set-variable/zarf.yaml b/src/test/packages/03-deprecated-set-variable/zarf.yaml index aa09069c11..aa614396f8 100644 --- a/src/test/packages/03-deprecated-set-variable/zarf.yaml +++ b/src/test/packages/03-deprecated-set-variable/zarf.yaml @@ -7,15 +7,14 @@ components: # Test that setVariable becomes setVariables - name: 1-test-deprecated-set-variable actions: - onDeploy: - before: - - cmd: echo "Hello Kitteh" - setVariable: HELLO_KITTEH - - cmd: echo "Hello from ${ZARF_VAR_HELLO_KITTEH}" + - when: BeforeDeploy + cmd: echo "Hello Kitteh" + setVariable: HELLO_KITTEH + - when: BeforeDeploy + cmd: echo "Hello from ${ZARF_VAR_HELLO_KITTEH}" # Test that ###ZARF_PKG_VAR_*### is still templated - name: 2-test-deprecated-pkg-var actions: - onDeploy: - before: - - cmd: echo "###ZARF_PKG_VAR_ECHO###" + - when: BeforeDeploy + cmd: echo "###ZARF_PKG_VAR_ECHO###" diff --git a/src/test/packages/04-file-folders-templating-sbom/zarf.yaml b/src/test/packages/04-file-folders-templating-sbom/zarf.yaml index 5aa4cb4ddf..e39a276b97 100644 --- a/src/test/packages/04-file-folders-templating-sbom/zarf.yaml +++ b/src/test/packages/04-file-folders-templating-sbom/zarf.yaml @@ -14,18 +14,18 @@ components: - source: include-files target: temp/include-files actions: - onDeploy: - after: - - cmd: cat temp/include-files/simple.txt - - cmd: cat temp/include-files/something.yaml - - cmd: rm -r temp + - when: AfterDeploy + cmd: | + cat temp/include-files/simple.txt + cat temp/include-files/something.yaml + rm -r temp - name: files required: true files: - source: requirements.txt target: temp/requirements.txt actions: - onDeploy: - after: - - cmd: cat temp/requirements.txt - - cmd: rm -r temp + - when: AfterDeploy + cmd: | + cat temp/requirements.txt + rm -r temp diff --git a/src/test/packages/05-multi-part/zarf.yaml b/src/test/packages/05-multi-part/zarf.yaml index 0fb4a75652..277dbc1d5d 100644 --- a/src/test/packages/05-multi-part/zarf.yaml +++ b/src/test/packages/05-multi-part/zarf.yaml @@ -8,9 +8,8 @@ components: required: true description: Include a 50 MB file needed to demonstrate a multi-part package actions: - onCreate: - before: - - cmd: dd if=/dev/urandom of=multi-part-demo.dat bs=1048576 count=50 + - when: BeforeCreate + cmd: dd if=/dev/urandom of=multi-part-demo.dat bs=1048576 count=50 files: - source: multi-part-demo.dat target: multi-part-demo.dat diff --git a/src/test/packages/09-composable-packages/sub-package/zarf.yaml b/src/test/packages/09-composable-packages/sub-package/zarf.yaml index 20b0a5b323..61ec82b737 100644 --- a/src/test/packages/09-composable-packages/sub-package/zarf.yaml +++ b/src/test/packages/09-composable-packages/sub-package/zarf.yaml @@ -38,9 +38,7 @@ components: container: podinfo path: /home/app/service.yaml actions: - onCreate: - before: - - cmd: ls - onDeploy: - after: - - cmd: cat coffee-ipsum.txt + - when: BeforeCreate + cmd: ls + - when: AfterDeploy + cmd: cat coffee-ipsum.txt diff --git a/src/test/packages/09-composable-packages/zarf.yaml b/src/test/packages/09-composable-packages/zarf.yaml index cf121120e6..c3be1fc59c 100644 --- a/src/test/packages/09-composable-packages/zarf.yaml +++ b/src/test/packages/09-composable-packages/zarf.yaml @@ -52,14 +52,12 @@ components: container: podinfo path: /home/app/service.yaml actions: - onCreate: - before: - - cmd: ls - onDeploy: - after: - - wait: - cluster: - kind: deployment - name: podinfo-compose-two - namespace: podinfo-compose-two - condition: available + - when: BeforeCreate + cmd: ls + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: podinfo-compose-two + namespace: podinfo-compose-two + condition: available diff --git a/src/test/packages/22-git-data/zarf.yaml b/src/test/packages/22-git-data/zarf.yaml index 2e3b56d75c..f882467af2 100644 --- a/src/test/packages/22-git-data/zarf.yaml +++ b/src/test/packages/22-git-data/zarf.yaml @@ -25,12 +25,9 @@ components: # The following performs a tag Git Repo Mirror forcing a fallback to host `git` - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test@v0.0.1 actions: - onDeploy: - before: - # Check to verify the package secret has been saved for the already deployed component - - cmd: test $(./zarf tools kubectl get secret -n zarf zarf-package-git-data-test -o jsonpath='{.data.*}' | base64 --decode | jq -r .deployedComponents | jq '. | length') -eq 2 - description: Check that the package secret has been updated with the deployed component - maxRetries: 3 + - when: BeforeDeploy + # Check to verify the package secret has been saved for the already deployed component + cmd: test $(./zarf tools kubectl get secret -n zarf zarf-package-git-data-test -o jsonpath='{.data.*}' | base64 --decode | jq -r .deployedComponents | jq '. | length') -eq 2 - name: specific-branch required: true @@ -40,12 +37,9 @@ components: # The following performs a branch Git Repo Mirror forcing a fallback to host `git` - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test@refs/heads/dragons actions: - onDeploy: - before: - # Check to verify the package secret has been saved for the already deployed component - - cmd: test $(./zarf tools kubectl get secret -n zarf zarf-package-git-data-test -o jsonpath='{.data.*}' | base64 --decode | jq -r .deployedComponents | jq '. | length') -eq 3 - description: Check that the package secret has been updated with the deployed component - maxRetries: 3 + - when: BeforeDeploy + # Check to verify the package secret has been saved for the already deployed component + cmd: test $(./zarf tools kubectl get secret -n zarf zarf-package-git-data-test -o jsonpath='{.data.*}' | base64 --decode | jq -r .deployedComponents | jq '. | length') -eq 3 - name: specific-hash required: true @@ -55,13 +49,8 @@ components: # The following performs a SHA Git Repo Mirror forcing a fallback to host `git` - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test@01a23218923f24194133b5eb11268cf8d73ff1bb actions: - onDeploy: - before: - # Check to verify the package secret has been saved for the already deployed component - - cmd: test $(./zarf tools kubectl get secret -n zarf zarf-package-git-data-test -o jsonpath='{.data.*}' | base64 --decode | jq -r .deployedComponents | jq '. | length') -eq 4 - description: Check that the package secret has been updated with the deployed component - maxRetries: 3 - onSuccess: - - cmd: test $(./zarf tools kubectl get secret -n zarf zarf-package-git-data-test -o jsonpath='{.data.*}' | base64 --decode | jq -r .deployedComponents | jq '. | length') -eq 4 - description: Check that the package secret has been updated with the deployed component - maxRetries: 3 + - when: BeforeDeploy + # Check to verify the package secret has been saved for the already deployed component + cmd: test $(./zarf tools kubectl get secret -n zarf zarf-package-git-data-test -o jsonpath='{.data.*}' | base64 --decode | jq -r .deployedComponents | jq '. | length') -eq 4 + - when: SuccessfulDeploy + cmd: test $(./zarf tools kubectl get secret -n zarf zarf-package-git-data-test -o jsonpath='{.data.*}' | base64 --decode | jq -r .deployedComponents | jq '. | length') -eq 4 diff --git a/src/test/packages/24-evil-variables/zarf.yaml b/src/test/packages/24-evil-variables/zarf.yaml index 906bced7c5..f4a33847a7 100644 --- a/src/test/packages/24-evil-variables/zarf.yaml +++ b/src/test/packages/24-evil-variables/zarf.yaml @@ -8,10 +8,9 @@ components: description: "###ZARF_PKG_TMPL_NUMB3R5###" required: true actions: - onDeploy: - before: - - cmd: echo "Hello Kitteh" - setVariables: - - name: HELLO_KITTEH - # "Hello Kitteh" should not match a 40 char 0-f shasum string. - pattern: "^[\\da-f]{64}$" + - when: BeforeDeploy + cmd: echo "Hello Kitteh" + setVariables: + - name: HELLO_KITTEH + # "Hello Kitteh" should not match a 40 char 0-f shasum string. + pattern: "^[\\da-f]{64}$" diff --git a/src/test/packages/25-evil-chart-lookup/zarf.yaml b/src/test/packages/25-evil-chart-lookup/zarf.yaml index b9f8a0441f..587af2c96a 100644 --- a/src/test/packages/25-evil-chart-lookup/zarf.yaml +++ b/src/test/packages/25-evil-chart-lookup/zarf.yaml @@ -15,18 +15,18 @@ components: images: - ghcr.io/stefanprodan/podinfo:6.4.0 actions: - onDeploy: - after: - - wait: - cluster: - kind: pod - name: app.kubernetes.io/name=podinfo - namespace: podinfo-from-oci - condition: ready - - wait: - cluster: - kind: deployment - # note this version is the same as the chart version - name: app.kubernetes.io/version=6.4.0 - namespace: podinfo-from-oci - condition: available + - when: AfterDeploy + wait: + cluster: + kind: pod + name: app.kubernetes.io/name=podinfo + namespace: podinfo-from-oci + condition: ready + - when: AfterDeploy + wait: + cluster: + kind: deployment + # note this version is the same as the chart version + name: app.kubernetes.io/version=6.4.0 + namespace: podinfo-from-oci + condition: available diff --git a/src/test/packages/25-local-tgz-chart/zarf.yaml b/src/test/packages/25-local-tgz-chart/zarf.yaml index 42158bc7be..e006572c8d 100644 --- a/src/test/packages/25-local-tgz-chart/zarf.yaml +++ b/src/test/packages/25-local-tgz-chart/zarf.yaml @@ -15,14 +15,12 @@ components: images: - ghcr.io/stefanprodan/podinfo:6.4.0 actions: - onCreate: - before: - - cmd: helm package ../../../../examples/helm-charts/chart/ - onDeploy: - after: - - wait: - cluster: - kind: deployment - name: podinfo - namespace: podinfo-from-local-chart - condition: available + - when: BeforeCreate + cmd: helm package ../../../../examples/helm-charts/chart/ + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: podinfo + namespace: podinfo-from-local-chart + condition: available diff --git a/src/test/packages/26-agent-ignore/zarf.yaml b/src/test/packages/26-agent-ignore/zarf.yaml index d945eef803..abf39f3d81 100644 --- a/src/test/packages/26-agent-ignore/zarf.yaml +++ b/src/test/packages/26-agent-ignore/zarf.yaml @@ -13,11 +13,10 @@ components: - manifests/deployment.yaml - manifests/namespace.yaml actions: - onDeploy: - after: - - wait: - cluster: - kind: deployment - name: httpd-deployment - namespace: httpd-ignored - condition: "{.status.readyReplicas}=2" + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: httpd-deployment + namespace: httpd-ignored + condition: "{.status.readyReplicas}=2" diff --git a/src/test/packages/31-component-actions-edgecases/zarf.yaml b/src/test/packages/31-component-actions-edgecases/zarf.yaml index 3f8e08264f..0e3be95b5e 100644 --- a/src/test/packages/31-component-actions-edgecases/zarf.yaml +++ b/src/test/packages/31-component-actions-edgecases/zarf.yaml @@ -7,11 +7,9 @@ components: - name: on-deploy required: true actions: - # runs during "zarf package remove" - onDeploy: - before: - # Wait for something that doesn't have a namespace (no -n) - - wait: - cluster: - kind: ns - name: zarf + - when: BeforeDeploy + # Wait for something that doesn't have a namespace (no -n) + wait: + cluster: + kind: ns + name: zarf diff --git a/src/test/packages/51-import-everything/zarf.yaml b/src/test/packages/51-import-everything/zarf.yaml index b69ef73086..051203c8f7 100644 --- a/src/test/packages/51-import-everything/zarf.yaml +++ b/src/test/packages/51-import-everything/zarf.yaml @@ -42,17 +42,18 @@ components: - source: https://raw.githubusercontent.com/defenseunicorns/zarf/main/README.md target: files/zarf-readme.md actions: - onDeploy: - after: - - cmd: test -f files/coffee-ipsum.txt - - cmd: test -f files/zarf-readme.md - onRemove: - before: - - cmd: rm files/coffee-ipsum.txt - - cmd: rm files/zarf-readme.md - after: - - cmd: test ! -f files/coffee-ipsum.txt - - cmd: test ! -f files/zarf-readme.md + - when: AfterDeploy + cmd: | + test -f files/coffee-ipsum.txt + test -f files/zarf-readme.md + - when: BeforeRemove + cmd: | + rm files/coffee-ipsum.txt + rm files/zarf-readme.md + - when: AfterRemove + cmd: | + test ! -f files/coffee-ipsum.txt + test ! -f files/zarf-readme.md # Test local charts (for skeletons) - name: local-chart-import @@ -66,11 +67,10 @@ components: images: - ghcr.io/stefanprodan/podinfo:6.4.0 actions: - onDeploy: - after: - - wait: - cluster: - kind: deployment - name: podinfo - namespace: local-chart - condition: available + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: podinfo + namespace: local-chart + condition: available diff --git a/src/test/upgrade/zarf.yaml b/src/test/upgrade/zarf.yaml index 5052a2bc55..14eda3f486 100644 --- a/src/test/upgrade/zarf.yaml +++ b/src/test/upgrade/zarf.yaml @@ -35,16 +35,18 @@ components: container: podinfo path: /home/app/service.yaml actions: - onDeploy: - after: - - cmd: cat podinfo-cosign.pub - mute: true - setVariable: PODINFO_COSIGN_PUB - - cmd: "echo \"Successfully deployed podinfo ###ZARF_PKG_VAR_PODINFO_VERSION### with the following cosign key:\\n\\n${ZARF_VAR_PODINFO_COSIGN_PUB}\"" - - cmd: rm podinfo-cosign.pub - - wait: - cluster: - kind: deployment - name: podinfo-upgrade - namespace: podinfo-upgrade - condition: available + - when: AfterDeploy + cmd: cat podinfo-cosign.pub + mute: true + setVariable: PODINFO_COSIGN_PUB + - when: AfterDeploy + cmd: | + "echo \"Successfully deployed podinfo ###ZARF_PKG_VAR_PODINFO_VERSION### with the following cosign key:\\n\\n${ZARF_VAR_PODINFO_COSIGN_PUB}\"" + rm podinfo-cosign.pub + - when: AfterDeploy + wait: + cluster: + kind: deployment + name: podinfo-upgrade + namespace: podinfo-upgrade + condition: available diff --git a/src/types/component.go b/src/types/component.go index 958d60e74d..65e42d2f0e 100644 --- a/src/types/component.go +++ b/src/types/component.go @@ -5,7 +5,6 @@ package types import ( - "github.com/defenseunicorns/zarf/src/pkg/utils/exec" "github.com/defenseunicorns/zarf/src/pkg/variables" "github.com/defenseunicorns/zarf/src/types/extensions" "github.com/invopop/jsonschema" @@ -64,7 +63,7 @@ type ZarfComponent struct { DeprecatedScripts DeprecatedZarfComponentScripts `json:"scripts,omitempty" jsonschema:"description=[Deprecated] (replaced by actions) Custom commands to run before or after package deployment. This will be removed in Zarf v1.0.0.,deprecated=true"` // Replaces scripts, fine-grained control over commands to run at various stages of a package lifecycle - Actions ZarfComponentActions `json:"actions,omitempty" jsonschema:"description=Custom commands to run at various stages of a package lifecycle"` + Actions []ZarfComponentAction `json:"actions,omitempty" jsonschema:"description=Custom commands to run at various stages of a package lifecycle"` } // RequiresCluster returns if the component requires a cluster connection to deploy @@ -156,45 +155,35 @@ type DeprecatedZarfComponentScripts struct { After []string `json:"after,omitempty" jsonschema:"description=Scripts to run after the component successfully deploys"` } -// ZarfComponentActions are ActionSets that map to different zarf package operations -type ZarfComponentActions struct { - OnCreate ZarfComponentActionSet `json:"onCreate,omitempty" jsonschema:"description=Actions to run during package creation"` - OnDeploy ZarfComponentActionSet `json:"onDeploy,omitempty" jsonschema:"description=Actions to run during package deployment"` - OnRemove ZarfComponentActionSet `json:"onRemove,omitempty" jsonschema:"description=Actions to run during package removal"` -} - -// ZarfComponentActionSet is a set of actions to run during a zarf package operation -type ZarfComponentActionSet struct { - Defaults ZarfComponentActionDefaults `json:"defaults,omitempty" jsonschema:"description=Default configuration for all actions in this set"` - Before []ZarfComponentAction `json:"before,omitempty" jsonschema:"description=Actions to run at the start of an operation"` - After []ZarfComponentAction `json:"after,omitempty" jsonschema:"description=Actions to run at the end of an operation"` - OnSuccess []ZarfComponentAction `json:"onSuccess,omitempty" jsonschema:"description=Actions to run if all operations succeed"` - OnFailure []ZarfComponentAction `json:"onFailure,omitempty" jsonschema:"description=Actions to run if all operations fail"` -} +type ActionPhase string -// ZarfComponentActionDefaults sets the default configs for child actions -type ZarfComponentActionDefaults struct { - Mute bool `json:"mute,omitempty" jsonschema:"description=Hide the output of commands during execution (default false)"` - MaxTotalSeconds int `json:"maxTotalSeconds,omitempty" jsonschema:"description=Default timeout in seconds for commands (default to 0, no timeout)"` - MaxRetries int `json:"maxRetries,omitempty" jsonschema:"description=Retry commands given number of times if they fail (default 0)"` - Dir string `json:"dir,omitempty" jsonschema:"description=Working directory for commands (default CWD)"` - Env []string `json:"env,omitempty" jsonschema:"description=Additional environment variables for commands"` - Shell exec.Shell `json:"shell,omitempty" jsonschema:"description=(cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems"` -} +const ( + BeforeCreate ActionPhase = "BeforeCreate" + AfterCreate ActionPhase = "AfterCreate" + BeforeDeploy ActionPhase = "BeforeDeploy" + AfterDeploy ActionPhase = "AfterDeploy" + FailedDeploy ActionPhase = "FailedDeploy" + BeforeRemove ActionPhase = "BeforeRemove" + AfterRemove ActionPhase = "AfterRemove" + FailedRemove ActionPhase = "FailedRemove" + SuccessfulRemove ActionPhase = "SuccessfulRemove" +) // ZarfComponentAction represents a single action to run during a zarf package operation type ZarfComponentAction struct { - Mute *bool `json:"mute,omitempty" jsonschema:"description=Hide the output of the command during package deployment (default false)"` - MaxTotalSeconds *int `json:"maxTotalSeconds,omitempty" jsonschema:"description=Timeout in seconds for the command (default to 0, no timeout for cmd actions and 300, 5 minutes for wait actions)"` - MaxRetries *int `json:"maxRetries,omitempty" jsonschema:"description=Retry the command if it fails up to given number of times (default 0)"` - Dir *string `json:"dir,omitempty" jsonschema:"description=The working directory to run the command in (default is CWD)"` - Env []string `json:"env,omitempty" jsonschema:"description=Additional environment variables to set for the command"` Cmd string `json:"cmd,omitempty" jsonschema:"description=The command to run. Must specify either cmd or wait for the action to do anything."` - Shell *exec.Shell `json:"shell,omitempty" jsonschema:"description=(cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems"` - DeprecatedSetVariable string `json:"setVariable,omitempty" jsonschema:"description=[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,pattern=^[A-Z0-9_]+$"` + Dir string `json:"dir,omitempty" jsonschema:"description=The working directory to run the command in (default is CWD)"` + Interpreter []string `json:"interpreter,omitempty" jsonschema:"description=A list of interpreter arguments used to execute the command. The first argument is the interpreter itself. It can be provided as a relative path to the current working directory or as an absolute path. The remaining arguments are appended prior to the command. This allows building command lines of the form \"/bin/bash\", \"-c\", \"echo foo\". If interpreter is unspecified, sensible defaults will be chosen based on the system OS."` + Env map[string]string `json:"env,omitempty" jsonschema:"description=Additional environment variables to set for the command"` SetVariables []variables.Variable `json:"setVariables,omitempty" jsonschema:"description=(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."` - Description string `json:"description,omitempty" jsonschema:"description=Description of the action to be displayed during package execution instead of the command"` + DeprecatedSetVariable string `json:"setVariable,omitempty" jsonschema:"description=[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,pattern=^[A-Z0-9_]+$"` Wait *ZarfComponentActionWait `json:"wait,omitempty" jsonschema:"description=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."` + When ActionPhase `json:"when" jsonschema:"description=When to run the action"` + Mute *bool `json:"mute,omitempty" jsonschema:"description=Hide the output of the command during package deployment (default false)"` +} + +func (a ZarfComponentAction) Phase() ActionPhase { + return a.When } // ZarfComponentActionWait specifies a condition to wait for before continuing diff --git a/src/types/validate.go b/src/types/validate.go index 131c556e39..e803ef47ab 100644 --- a/src/types/validate.go +++ b/src/types/validate.go @@ -139,10 +139,6 @@ func (pkg ZarfPackage) Validate() error { } } - if actionsErr := component.Actions.validate(); actionsErr != nil { - err = errors.Join(err, fmt.Errorf("%q: %w", component.Name, actionsErr)) - } - // ensure groups don't have multiple defaults or only one component if component.DeprecatedGroup != "" { if component.Default { @@ -164,26 +160,6 @@ func (pkg ZarfPackage) Validate() error { return err } -func (a ZarfComponentActions) validate() error { - var err error - - err = errors.Join(err, a.OnCreate.Validate()) - - if a.OnCreate.HasSetVariables() { - err = errors.Join(err, fmt.Errorf("cannot contain setVariables outside of onDeploy in actions")) - } - - err = errors.Join(err, a.OnDeploy.Validate()) - - if a.OnRemove.HasSetVariables() { - err = errors.Join(err, fmt.Errorf("cannot contain setVariables outside of onDeploy in actions")) - } - - err = errors.Join(err, a.OnRemove.Validate()) - - return err -} - // Validate validates the component trying to be imported. func (c ZarfComponent) Validate() error { var err error @@ -219,39 +195,6 @@ func (c ZarfComponent) Validate() error { return err } -// HasSetVariables returns true if any of the actions contain setVariables. -func (as ZarfComponentActionSet) HasSetVariables() bool { - check := func(actions []ZarfComponentAction) bool { - for _, action := range actions { - if len(action.SetVariables) > 0 { - return true - } - } - return false - } - - return check(as.Before) || check(as.After) || check(as.OnSuccess) || check(as.OnFailure) -} - -// Validate runs all validation checks on component action sets. -func (as ZarfComponentActionSet) Validate() error { - var err error - validate := func(actions []ZarfComponentAction) { - for _, action := range actions { - if actionErr := action.Validate(); actionErr != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrAction, actionErr)) - } - - } - } - - validate(as.Before) - validate(as.After) - validate(as.OnFailure) - validate(as.OnSuccess) - return err -} - // Validate runs all validation checks on an action. func (action ZarfComponentAction) Validate() error { var err error diff --git a/src/types/validate_test.go b/src/types/validate_test.go index 64af534f58..9a3d456fdc 100644 --- a/src/types/validate_test.go +++ b/src/types/validate_test.go @@ -275,105 +275,6 @@ func TestValidateChart(t *testing.T) { } } -func TestValidateComponentActions(t *testing.T) { - t.Parallel() - tests := []struct { - name string - actions ZarfComponentActions - expectedErrs []string - }{ - { - name: "valid actions", - actions: ZarfComponentActions{ - OnCreate: ZarfComponentActionSet{ - Before: []ZarfComponentAction{ - { - Cmd: "echo 'onCreate before valid'", - }, - }, - }, - OnDeploy: ZarfComponentActionSet{ - Before: []ZarfComponentAction{ - { - Cmd: "echo 'onDeploy before valid'", - }, - }, - }, - }, - expectedErrs: nil, - }, - { - name: "setVariables in onCreate", - actions: ZarfComponentActions{ - OnCreate: ZarfComponentActionSet{ - Before: []ZarfComponentAction{ - { - Cmd: "echo 'invalid setVariable'", - SetVariables: []variables.Variable{{Name: "VAR"}}, - }, - }, - }, - }, - expectedErrs: []string{"cannot contain setVariables outside of onDeploy in actions"}, - }, - { - name: "invalid onCreate action", - actions: ZarfComponentActions{ - OnCreate: ZarfComponentActionSet{ - Before: []ZarfComponentAction{ - { - Cmd: "create", - Wait: &ZarfComponentActionWait{Cluster: &ZarfComponentActionWaitCluster{}}, - }, - }, - }, - OnDeploy: ZarfComponentActionSet{ - After: []ZarfComponentAction{ - { - Cmd: "deploy", - Wait: &ZarfComponentActionWait{Cluster: &ZarfComponentActionWaitCluster{}}, - }, - }, - }, - OnRemove: ZarfComponentActionSet{ - OnSuccess: []ZarfComponentAction{ - { - Cmd: "remove", - Wait: &ZarfComponentActionWait{Cluster: &ZarfComponentActionWaitCluster{}}, - }, - }, - OnFailure: []ZarfComponentAction{ - { - Cmd: "remove2", - Wait: &ZarfComponentActionWait{Cluster: &ZarfComponentActionWaitCluster{}}, - }, - }, - }, - }, - 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(), - }, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - err := tt.actions.validate() - if tt.expectedErrs == nil { - require.NoError(t, err) - return - } - errs := strings.Split(err.Error(), "\n") - require.ElementsMatch(t, tt.expectedErrs, errs) - }) - } -} - func TestValidateComponentAction(t *testing.T) { t.Parallel() tests := []struct { diff --git a/zarf.schema.json b/zarf.schema.json index 3004a424b1..800457184c 100644 --- a/zarf.schema.json +++ b/zarf.schema.json @@ -165,49 +165,6 @@ "^x-": {} } }, - "Shell": { - "properties": { - "windows": { - "type": "string", - "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)", - "examples": [ - "powershell", - "cmd", - "pwsh", - "sh", - "bash", - "gsh" - ] - }, - "linux": { - "type": "string", - "description": "(default 'sh') Indicates a preference for the shell to use on Linux systems", - "examples": [ - "sh", - "bash", - "fish", - "zsh", - "pwsh" - ] - }, - "darwin": { - "type": "string", - "description": "(default 'sh') Indicates a preference for the shell to use on macOS systems", - "examples": [ - "sh", - "bash", - "fish", - "zsh", - "pwsh" - ] - } - }, - "additionalProperties": false, - "type": "object", - "patternProperties": { - "^x-": {} - } - }, "Variable": { "properties": { "name": { @@ -501,7 +458,10 @@ "description": "[Deprecated] (replaced by actions) Custom commands to run before or after package deployment. This will be removed in Zarf v1.0.0." }, "actions": { - "$ref": "#/$defs/ZarfComponentActions", + "items": { + "$ref": "#/$defs/ZarfComponentAction" + }, + "type": "array", "description": "Custom commands to run at various stages of a package lifecycle" } }, @@ -516,41 +476,27 @@ }, "ZarfComponentAction": { "properties": { - "mute": { - "type": "boolean", - "description": "Hide the output of the command during package deployment (default false)" - }, - "maxTotalSeconds": { - "type": "integer", - "description": "Timeout in seconds for the command (default to 0" - }, - "maxRetries": { - "type": "integer", - "description": "Retry the command if it fails up to given number of times (default 0)" + "cmd": { + "type": "string", + "description": "The command to run. Must specify either cmd or wait for the action to do anything." }, "dir": { "type": "string", "description": "The working directory to run the command in (default is CWD)" }, - "env": { + "interpreter": { "items": { "type": "string" }, "type": "array", - "description": "Additional environment variables to set for the command" - }, - "cmd": { - "type": "string", - "description": "The command to run. Must specify either cmd or wait for the action to do anything." - }, - "shell": { - "$ref": "#/$defs/Shell", - "description": "(cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems" + "description": "A list of interpreter arguments used to execute the command. The first argument is the interpreter itself. It can be provided as a relative path to the current working directory or as an absolute path. The remaining arguments are appended prior to the command. This allows building command lines of the form \"/bin/bash\"" }, - "setVariable": { - "type": "string", - "pattern": "^[A-Z0-9_]+$", - "description": "[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" + "env": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "Additional environment variables to set for the command" }, "setVariables": { "items": { @@ -559,94 +505,29 @@ "type": "array", "description": "(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." }, - "description": { + "setVariable": { "type": "string", - "description": "Description of the action to be displayed during package execution instead of the command" + "pattern": "^[A-Z0-9_]+$", + "description": "[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" }, "wait": { "$ref": "#/$defs/ZarfComponentActionWait", "description": "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." - } - }, - "additionalProperties": false, - "type": "object", - "patternProperties": { - "^x-": {} - } - }, - "ZarfComponentActionDefaults": { - "properties": { - "mute": { - "type": "boolean", - "description": "Hide the output of commands during execution (default false)" - }, - "maxTotalSeconds": { - "type": "integer", - "description": "Default timeout in seconds for commands (default to 0" - }, - "maxRetries": { - "type": "integer", - "description": "Retry commands given number of times if they fail (default 0)" }, - "dir": { + "when": { "type": "string", - "description": "Working directory for commands (default CWD)" - }, - "env": { - "items": { - "type": "string" - }, - "type": "array", - "description": "Additional environment variables for commands" - }, - "shell": { - "$ref": "#/$defs/Shell", - "description": "(cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems" - } - }, - "additionalProperties": false, - "type": "object", - "patternProperties": { - "^x-": {} - } - }, - "ZarfComponentActionSet": { - "properties": { - "defaults": { - "$ref": "#/$defs/ZarfComponentActionDefaults", - "description": "Default configuration for all actions in this set" - }, - "before": { - "items": { - "$ref": "#/$defs/ZarfComponentAction" - }, - "type": "array", - "description": "Actions to run at the start of an operation" + "description": "When to run the action" }, - "after": { - "items": { - "$ref": "#/$defs/ZarfComponentAction" - }, - "type": "array", - "description": "Actions to run at the end of an operation" - }, - "onSuccess": { - "items": { - "$ref": "#/$defs/ZarfComponentAction" - }, - "type": "array", - "description": "Actions to run if all operations succeed" - }, - "onFailure": { - "items": { - "$ref": "#/$defs/ZarfComponentAction" - }, - "type": "array", - "description": "Actions to run if all operations fail" + "mute": { + "type": "boolean", + "description": "Hide the output of the command during package deployment (default false)" } }, "additionalProperties": false, "type": "object", + "required": [ + "when" + ], "patternProperties": { "^x-": {} } @@ -747,27 +628,6 @@ "^x-": {} } }, - "ZarfComponentActions": { - "properties": { - "onCreate": { - "$ref": "#/$defs/ZarfComponentActionSet", - "description": "Actions to run during package creation" - }, - "onDeploy": { - "$ref": "#/$defs/ZarfComponentActionSet", - "description": "Actions to run during package deployment" - }, - "onRemove": { - "$ref": "#/$defs/ZarfComponentActionSet", - "description": "Actions to run during package removal" - } - }, - "additionalProperties": false, - "type": "object", - "patternProperties": { - "^x-": {} - } - }, "ZarfComponentExtensions": { "properties": { "bigbang": {