diff --git a/.github/workflows/test-command.yml b/.github/workflows/test-command.yml index 75d9acb680..bf91f359fc 100644 --- a/.github/workflows/test-command.yml +++ b/.github/workflows/test-command.yml @@ -457,103 +457,6 @@ jobs: GITHUB_REF: ${{ github.event.client_payload.pull_request.head.ref }} GITHUB_OWNER: ${{ github.event.client_payload.github.payload.repository.owner.login }} - # Run the E2E test of a Git-based Helm chart - e2e-git-based-helm-chart: - runs-on: ubuntu-latest - needs: [parse, build] - if: needs.parse.outputs.run-e2e == 'true' - container: cloudposse/test-harness:latest - steps: - # Update GitHub status for pending pipeline run - - name: "Update GitHub Status for pending" - uses: docker://cloudposse/github-status-updater - with: - args: "-action update_state -ref ${{ github.event.client_payload.pull_request.head.sha }} -repo ${{ github.event.client_payload.github.payload.repository.name }}" - env: - GITHUB_TOKEN: ${{ secrets.PAT }} - GITHUB_STATE: pending - GITHUB_CONTEXT: "/test e2e - Git-Based Helm Chart" - GITHUB_DESCRIPTION: "started by @${{ github.event.client_payload.github.actor }}" - GITHUB_TARGET_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - GITHUB_REF: ${{ github.event.client_payload.pull_request.head.ref }} - GITHUB_OWNER: ${{ github.event.client_payload.github.payload.repository.owner.login }} - - # Checkout the code from GitHub Pull Request - - name: "Checkout the code" - uses: actions/checkout@v2 - with: - token: ${{ secrets.PAT }} - repository: ${{ github.event.client_payload.pull_request.head.repo.full_name }} - ref: ${{ github.event.client_payload.pull_request.head.ref }} - - # Download the built artifacts - - name: "Download the built artifacts" - uses: actions/download-artifact@v2 - - - name: "Run E2E tests" - shell: bash -x -e -o pipefail {0} - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_DEFENSEUNICORNS_COMMERCIAL_SA_ZARF }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_DEFENSEUNICORNS_COMMERCIAL_SA_ZARF }} - AWS_DEFAULT_REGION: us-east-1 - run: | - # cloudposse/test-harness has golang 1.15, we need 1.16. This is the easiest way I know to do it. This should definitely be revisited and cleaned up. - git clone --branch v0.8.0 --depth 1 https://github.com/asdf-vm/asdf.git $HOME/.asdf - source ~/.asdf/asdf.sh - export PATH="$HOME/.asdf/bin:$PATH" - asdf plugin-add golang https://github.com/kennyp/asdf-golang.git - asdf install golang 1.16.7 - asdf global golang 1.16.7 - export GOPATH="$HOME/go" - export PATH="$PATH:$GOPATH/bin" - chmod +x build/zarf - ./build/zarf tools registry login registry1.dso.mil --username "${{ secrets.REGISTRY1_USERNAME_ZARF_ROBOT }}" --password "${{ secrets.REGISTRY1_PASSWORD_ZARF_ROBOT }}" - make test-cloud-e2e-git-based-helm-chart - - # Update GitHub status for failing pipeline run - - name: "Update GitHub Status for failure" - if: ${{ failure() }} - uses: docker://cloudposse/github-status-updater - with: - args: "-action update_state -ref ${{ github.event.client_payload.pull_request.head.sha }} -repo ${{ github.event.client_payload.github.payload.repository.name }}" - env: - GITHUB_TOKEN: ${{ secrets.PAT }} - GITHUB_STATE: failure - GITHUB_CONTEXT: "/test e2e - Git-Based Helm Chart" - GITHUB_DESCRIPTION: "run failed" - GITHUB_TARGET_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - GITHUB_REF: ${{ github.event.client_payload.pull_request.head.ref }} - GITHUB_OWNER: ${{ github.event.client_payload.github.payload.repository.owner.login }} - - # Update GitHub status for successful pipeline run - - name: "Update GitHub Status for success" - uses: docker://cloudposse/github-status-updater - with: - args: "-action update_state -ref ${{ github.event.client_payload.pull_request.head.sha }} -repo ${{ github.event.client_payload.github.payload.repository.name }}" - env: - GITHUB_TOKEN: ${{ secrets.PAT }} - GITHUB_STATE: success - GITHUB_CONTEXT: "/test e2e - Git-Based Helm Chart" - GITHUB_DESCRIPTION: "run passed" - GITHUB_TARGET_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - GITHUB_REF: ${{ github.event.client_payload.pull_request.head.ref }} - GITHUB_OWNER: ${{ github.event.client_payload.github.payload.repository.owner.login }} - - # Update GitHub status for cancelled pipeline run - - name: "Update GitHub Status for cancelled" - if: ${{ cancelled() }} - uses: docker://cloudposse/github-status-updater - with: - args: "-action update_state -ref ${{ github.event.client_payload.pull_request.head.sha }} -repo ${{ github.event.client_payload.github.payload.repository.name }}" - env: - GITHUB_TOKEN: ${{ secrets.PAT }} - GITHUB_STATE: error - GITHUB_CONTEXT: "/test e2e - Git-Based Helm Chart" - GITHUB_DESCRIPTION: "run cancelled" - GITHUB_TARGET_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - GITHUB_REF: ${{ github.event.client_payload.pull_request.head.ref }} - GITHUB_OWNER: ${{ github.event.client_payload.github.payload.repository.owner.login }} - # Run E2E test for general CLI stuff e2e-general-cli: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index edf9e25b3f..61e478f916 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ rpms/ data/ *.vbox bundle/ -charts/ .idea/ .tool-versions test/tf/public-ec2-instance/.test-data @@ -22,3 +21,6 @@ test/tf/public-ec2-instance/.terraform terraform.tfstate terraform.tfstate.backup .terraform.lock.hcl + +.zarf* +zarf-pki \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c4f9ff3ea5..bc8e03a0df 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,7 @@ repos: rev: v4.0.1 hooks: - id: check-added-large-files + args: ['--maxkb=1024'] - id: check-merge-conflict - id: detect-aws-credentials args: diff --git a/.vscode/launch.json b/.vscode/launch.json index 6908e48e57..15d1f0f187 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,9 +12,8 @@ "program": "${workspaceFolder}/cli", "env": {}, "args": [ - "package", - "create", - "--confirm" + "connect", + "doom" ] }, diff --git a/Makefile b/Makefile index 8591a6d28e..7d8e3530ae 100644 --- a/Makefile +++ b/Makefile @@ -90,9 +90,11 @@ test-cloud-e2e-gitops: package-example-gitops-data ## E2E test of Gitops example test-cloud-e2e-data-injection: package-example-data-injection ## E2E test of the Data Injection example. Requires access to an AWS account. Costs money. Make sure you ran the `build-cli` and `init-package` targets first cd test/e2e && go test ./... -run TestDataInjection -v -timeout 1200s +################ BEGIN Pending removal post-merge .PHONY: test-cloud-e2e-git-based-helm-chart -test-cloud-e2e-git-based-helm-chart: package-example-single-big-bang-package ## E2E test of the Data Injection example. Requires access to an AWS account. Costs money. Make sure you ran the `build-cli` and `init-package` targets first - cd test/e2e && go test ./... -run TestGitBasedHelmChart -v -timeout 1200s +test-cloud-e2e-git-based-helm-chart: + echo done +################ END Pending removal post-merge .PHONY: test-cloud-e2e-general-cli test-cloud-e2e-general-cli: ## Runs tests of the CLI that don't need a cluster diff --git a/assets/charts/gitea-values.yaml b/assets/charts/gitea-values.yaml new file mode 100644 index 0000000000..3d3f5cce0c --- /dev/null +++ b/assets/charts/gitea-values.yaml @@ -0,0 +1,37 @@ +persistence: + storageClass: "###ZARF_STORAGE_CLASS###" +gitea: + admin: + username: "zarf-git-user" + password: "###ZARF_GIT_AUTH_PUSH###" + email: "zarf@localhost" + cache: + builtIn: + enabled: false + config: + APP_NAME: "Zarf Gitops Service" + server: + DISABLE_SSH: true + OFFLINE_MODE: true + database: + DB_TYPE: sqlite3 + # Note that the init script checks to see if the IP & port of the database service is accessible, so make sure you set those to something that resolves as successful (since sqlite uses files on disk setting the port & ip won't affect the running of gitea). + HOST: docker-registry.zarf.svc.cluster.local:5000 + security: + INSTALL_LOCK: true + service: + DISABLE_REGISTRATION: true + repository: + ENABLE_PUSH_CREATE_USER: true + FORCE_PRIVATE: true + database: + builtIn: + postgresql: + enabled: false +resources: + requests: + cpu: "200m" + memory: "512Mi" + limits: + cpu: "1" + memory: "2Gi" diff --git a/assets/charts/pgl-values.yaml b/assets/charts/pgl-values.yaml new file mode 100644 index 0000000000..cc71eb10db --- /dev/null +++ b/assets/charts/pgl-values.yaml @@ -0,0 +1,31 @@ +grafana: + enabled: true + adminUser: "zarf-admin" + adminPassword: "###ZARF_LOGGING_AUTH###" + grafana.ini: + server: + root_url: "%(protocol)s://%(domain)s/monitor" + serve_from_sub_path: true +promtail: + extraScrapeConfigs: + - job_name: journal + journal: + max_age: 12h + labels: + job: systemd-journal + relabel_configs: + - source_labels: ["__journal__systemd_unit"] + target_label: "unit" + - source_labels: ["__journal__hostname"] + target_label: "hostname" + + # Mount journal directory into promtail pods + extraVolumes: + - name: journal + hostPath: + path: /var/log/journal + + extraVolumeMounts: + - name: journal + mountPath: /var/log/journal + readOnly: true diff --git a/assets/charts/registry-values-seed.yaml b/assets/charts/registry-values-seed.yaml new file mode 100644 index 0000000000..88c6586c46 --- /dev/null +++ b/assets/charts/registry-values-seed.yaml @@ -0,0 +1,2 @@ +image: + repository: "###ZARF_SEED_REGISTRY###/library/registry" diff --git a/assets/charts/registry-values.yaml b/assets/charts/registry-values.yaml new file mode 100644 index 0000000000..bb33b3f767 --- /dev/null +++ b/assets/charts/registry-values.yaml @@ -0,0 +1,20 @@ +persistence: + enabled: true + storageClass: "###ZARF_STORAGE_CLASS###" +image: + repository: "###ZARF_REGISTRY###/library/registry" +secrets: + htpasswd: "###ZARF_HTPASSWD###" +# https://github.com/containerd/containerd/blob/v1.5.8/pkg/cri/server/image_pull.go#L412 +# thx containerd *magic* :-D +# tlsSecretName: tls-pem +service: + type: NodePort + nodePort: "###ZARF_REGISTRY_NODEPORT###" +resources: + requests: + cpu: "500m" + memory: "256Mi" + limits: + cpu: "3" + memory: "2Gi" diff --git a/assets/manifests/gitops/gitea.yaml b/assets/manifests/gitops/gitea.yaml deleted file mode 100644 index c941038107..0000000000 --- a/assets/manifests/gitops/gitea.yaml +++ /dev/null @@ -1,74 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: git ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: git-ingress - namespace: git - annotations: - kubernetes.io/ingress.class: "traefik" - traefik.ingress.kubernetes.io/router.middlewares: kube-system-ssl-redirect@kubernetescrd -spec: - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: stuart-gitea-http - port: - number: 3000 ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: stuart - namespace: git -spec: - chart: https://%{KUBERNETES_API}%/static/charts/gitea-2.2.5.tgz - targetNamespace: git - valuesContent: |- - persistence: - storageClass: local-path - image: - pullPolicy: Never - gitea: - admin: - username: "zarf-git-user" - password: "###ZARF_SECRET###" - email: "zarf@localhost" - cache: - builtIn: - enabled: false - config: - APP_NAME: "Zarf Gitops Service" - server: - DISABLE_SSH: true - OFFLINE_MODE: true - database: - DB_TYPE: sqlite3 - # Note that the init script checks to see if the IP & port of the database service is accessible, so make sure you set those to something that resolves as successful (since sqlite uses files on disk setting the port & ip won't affect the running of gitea). - HOST: kevin-docker-registry.registry.svc.cluster.local:5000 - security: - INSTALL_LOCK: true - service: - DISABLE_REGISTRATION: true - repository: - ENABLE_PUSH_CREATE_USER: true - FORCE_PRIVATE: true - database: - builtIn: - postgresql: - enabled: false - resources: - requests: - cpu: "200m" - memory: "512Mi" - limits: - cpu: "1" - memory: "2Gi" diff --git a/assets/manifests/logging/pgl-stack.yaml b/assets/manifests/logging/pgl-stack.yaml deleted file mode 100644 index 82409d7ad3..0000000000 --- a/assets/manifests/logging/pgl-stack.yaml +++ /dev/null @@ -1,68 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: logging ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: grafana-ingress - namespace: logging - annotations: - kubernetes.io/ingress.class: "traefik" - traefik.ingress.kubernetes.io/router.middlewares: kube-system-ssl-redirect@kubernetescrd -spec: - rules: - - http: - paths: - - path: /monitor - pathType: Prefix - backend: - service: - name: loki-grafana - port: - number: 80 ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: loki - namespace: logging -spec: - chart: https://%{KUBERNETES_API}%/static/charts/loki-stack-2.4.1.tgz - targetNamespace: logging - valuesContent: |- - grafana: - enabled: true - adminUser: "zarf-admin" - adminPassword: "###ZARF_SECRET###" - grafana.ini: - server: - root_url: "%(protocol)s://%(domain)s/monitor" - serve_from_sub_path: true - promtail: - extraScrapeConfigs: - - job_name: journal - journal: - max_age: 12h - labels: - job: systemd-journal - relabel_configs: - - source_labels: ['__journal__systemd_unit'] - target_label: 'unit' - - source_labels: ['__journal__hostname'] - target_label: 'hostname' - - # Mount journal directory into promtail pods - extraVolumes: - - name: journal - hostPath: - path: /var/log/journal - - extraVolumeMounts: - - name: journal - mountPath: /var/log/journal - readOnly: true - image: - pullPolicy: Never diff --git a/assets/manifests/registry/configmap.yaml b/assets/manifests/registry/configmap.yaml new file mode 100644 index 0000000000..02c9b9bb0b --- /dev/null +++ b/assets/manifests/registry/configmap.yaml @@ -0,0 +1,10 @@ +# https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry +apiVersion: v1 +kind: ConfigMap +metadata: + name: local-registry-hosting + namespace: kube-public +data: + localRegistryHosting.v1: | + host: "###ZARF_REGISTRY###" + help: "https://github.com/defenseunicorns/zarf" diff --git a/assets/manifests/registry/registry.yaml b/assets/manifests/registry/registry.yaml deleted file mode 100644 index f76c5ed748..0000000000 --- a/assets/manifests/registry/registry.yaml +++ /dev/null @@ -1,49 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: registry ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: registry-ingress - namespace: registry - annotations: - kubernetes.io/ingress.class: "traefik" - traefik.ingress.kubernetes.io/router.middlewares: kube-system-ssl-redirect@kubernetescrd -spec: - rules: - - http: - paths: - - path: /v2/ - pathType: Prefix - backend: - service: - name: kevin-docker-registry - port: - number: 5000 ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: kevin - namespace: registry -spec: - chart: https://%{KUBERNETES_API}%/static/charts/docker-registry-1.10.1.tgz - targetNamespace: registry - valuesContent: |- - persistence: - enabled: true - image: - repository: registry1.dso.mil/ironbank/opensource/docker/registry-v2 - pullPolicy: Never - secrets: - htpasswd: ###ZARF_HTPASSWD### - resources: - requests: - cpu: "100m" - memory: "512Mi" - limits: - cpu: "1" - memory: "2Gi" diff --git a/assets/manifests/traefik/traefik-tls.yaml b/assets/manifests/traefik/traefik-tls.yaml deleted file mode 100644 index 513e40405a..0000000000 --- a/assets/manifests/traefik/traefik-tls.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: traefik.containo.us/v1alpha1 -kind: TLSStore -metadata: - name: default - namespace: kube-system -spec: - defaultCertificate: - secretName: tls-pem ---- -apiVersion: traefik.containo.us/v1alpha1 -kind: Middleware -metadata: - name: ssl-redirect - namespace: kube-system -spec: - redirectRegex: - regex: ^http://(.*) - replacement: https://${1} - permanent: true diff --git a/assets/manifests/traefik/traefik.yaml b/assets/manifests/traefik/traefik.yaml deleted file mode 100644 index cebf7f9089..0000000000 --- a/assets/manifests/traefik/traefik.yaml +++ /dev/null @@ -1,36 +0,0 @@ ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: traefik - namespace: kube-system -spec: - chart: https://%{KUBERNETES_API}%/static/charts/traefik-9.18.2.tgz - targetNamespace: kube-system - valuesContent: |- - rbac: - enabled: true - ports: - websecure: - tls: - enabled: true - podAnnotations: - prometheus.io/port: "8082" - prometheus.io/scrape: "true" - providers: - kubernetesIngress: - publishedService: - enabled: true - priorityClassName: "system-cluster-critical" - image: - name: "rancher/library-traefik" - tolerations: - - key: "CriticalAddonsOnly" - operator: "Exists" - - key: "node-role.kubernetes.io/control-plane" - operator: "Exists" - effect: "NoSchedule" - - key: "node-role.kubernetes.io/master" - operator: "Exists" - effect: "NoSchedule" - diff --git a/assets/misc/k9s-theme.yaml b/assets/misc/k9s-theme.yaml deleted file mode 100644 index c8fa1c1104..0000000000 --- a/assets/misc/k9s-theme.yaml +++ /dev/null @@ -1,111 +0,0 @@ -# original source: https://raw.githubusercontent.com/derailed/k9s/v0.24.14/skins/dracula.yml -foreground: &foreground "#f8f8f2" -background: &background "#282a36" -current_line: ¤t_line "#44475a" -selection: &selection "#44475a" -comment: &comment "#6272a4" -cyan: &cyan "#8be9fd" -green: &green "#50fa7b" -orange: &orange "#ffb86c" -pink: &pink "#ff79c6" -purple: &purple "#bd93f9" -red: &red "#ff5555" -yellow: &yellow "#f1fa8c" - -# Skin... -k9s: - # General K9s styles - body: - fgColor: *foreground - bgColor: *background - logoColor: *purple - # Command prompt styles - prompt: - fgColor: *foreground - bgColor: *background - suggestColor: *purple - # ClusterInfoView styles. - info: - fgColor: *pink - sectionColor: *foreground - # Dialog styles. - dialog: - fgColor: *foreground - bgColor: *background - buttonFgColor: *foreground - buttonBgColor: *purple - buttonFocusFgColor: *yellow - buttonFocusBgColor: *pink - labelFgColor: *orange - fieldFgColor: *foreground - frame: - # Borders styles. - border: - fgColor: *selection - focusColor: *current_line - menu: - fgColor: *foreground - keyColor: *pink - # Used for favorite namespaces - numKeyColor: *pink - # CrumbView attributes for history navigation. - crumbs: - fgColor: *foreground - bgColor: *current_line - activeColor: *current_line - # Resource status and update styles - status: - newColor: *cyan - modifyColor: *purple - addColor: *green - errorColor: *red - highlightcolor: *orange - killColor: *comment - completedColor: *comment - # Border title styles. - title: - fgColor: *foreground - bgColor: *current_line - highlightColor: *orange - counterColor: *purple - filterColor: *pink - views: - # Charts skins... - charts: - bgColor: default - defaultDialColors: - - *purple - - *red - defaultChartColors: - - *purple - - *red - # TableView attributes. - table: - fgColor: *foreground - bgColor: *background - cursorFgColor: *foreground - cursorBgColor: *current_line - # Header row styles. - header: - fgColor: *foreground - bgColor: *background - sorterColor: *cyan - # Xray view attributes. - xray: - fgColor: *foreground - bgColor: *background - cursorColor: *current_line - graphicColor: *purple - showIcons: false - # YAML info styles. - yaml: - keyColor: *pink - colonColor: *purple - valueColor: *foreground - # Logs styles. - logs: - fgColor: *foreground - bgColor: *background - indicator: - fgColor: *foreground - bgColor: *purple diff --git a/assets/misc/registries.yaml b/assets/misc/registries.yaml deleted file mode 100644 index 16840c3507..0000000000 --- a/assets/misc/registries.yaml +++ /dev/null @@ -1,19 +0,0 @@ -mirrors: - registry.dso.mil: - endpoint: - - "https://###ZARF_TARGET_ENDPOINT###" - registry1.dso.mil: - endpoint: - - "https://###ZARF_TARGET_ENDPOINT###" - docker.io: - endpoint: - - "https://###ZARF_TARGET_ENDPOINT###" - registry-1.docker.io: - endpoint: - - "https://###ZARF_TARGET_ENDPOINT###" - ghcr.io: - endpoint: - - "https://###ZARF_TARGET_ENDPOINT###" - registry.opensource.zalan.do: - endpoint: - - "https://###ZARF_TARGET_ENDPOINT###" diff --git a/assets/scripts/k3s.service b/assets/scripts/k3s.service index a27ba9da0f..ddbf47b8c3 100644 --- a/assets/scripts/k3s.service +++ b/assets/scripts/k3s.service @@ -24,4 +24,4 @@ RestartSec=5s ExecStartPre=/bin/sh -xc '! /usr/bin/systemctl is-enabled --quiet nm-cloud-setup.service' ExecStartPre=-/sbin/modprobe br_netfilter ExecStartPre=-/sbin/modprobe overlay -ExecStart=/usr/local/bin/k3s server --write-kubeconfig-mode=700 +ExecStart=/usr/local/bin/k3s server --write-kubeconfig-mode=700 --disable traefik diff --git a/cli/cmd/connect.go b/cli/cmd/connect.go new file mode 100644 index 0000000000..e78d138288 --- /dev/null +++ b/cli/cmd/connect.go @@ -0,0 +1,37 @@ +package cmd + +import ( + "github.com/defenseunicorns/zarf/cli/internal/k8s" + "github.com/spf13/cobra" +) + +var ( + connectResourceName string + connectNamespace string + connectResourceType string + connectLocalPort int + connectRemotePort int + + connectCmd = &cobra.Command{ + Use: "connect ", + Aliases: []string{"c"}, + Short: "Access services or pods deployed in the cluster.", + Run: func(cmd *cobra.Command, args []string) { + var target string + if len(args) > 0 { + target = args[0] + } + tunnel := k8s.NewTunnel(connectNamespace, connectResourceType, connectResourceName, connectLocalPort, connectRemotePort) + tunnel.Connect(target, true) + }, + } +) + +func init() { + rootCmd.AddCommand(connectCmd) + connectCmd.Flags().StringVar(&connectResourceName, "name", "docker-registry", "Specify the resource name. E.g. name=unicorns or name=unicorn-pod-7448499f4d-b5bk6") + connectCmd.Flags().StringVar(&connectNamespace, "namespace", k8s.ZarfNamespace, "Specify the namespace. E.g. namespace=default") + connectCmd.Flags().StringVar(&connectResourceType, "type", k8s.SvcResource, "Specify the resource type. E.g. type=svc or type=pod") + connectCmd.Flags().IntVar(&connectLocalPort, "local-port", 0, "(Optional, autogenerated if not provided) Specify the local port to bind to. E.g. local-port=42000") + connectCmd.Flags().IntVar(&connectRemotePort, "remote-port", 0, "Specify the remote port of the resource to bind to. E.g. remote-port=8080") +} diff --git a/cli/cmd/destroy.go b/cli/cmd/destroy.go index be38b9374c..25aee943fa 100644 --- a/cli/cmd/destroy.go +++ b/cli/cmd/destroy.go @@ -1,48 +1,52 @@ package cmd import ( - "fmt" + "github.com/defenseunicorns/zarf/cli/internal/helm" "os" "regexp" - "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/k8s" "github.com/defenseunicorns/zarf/cli/internal/utils" "github.com/spf13/cobra" ) var confirmDestroy bool +var removeComponents bool var destroyCmd = &cobra.Command{ - Use: "destroy", - Short: "Tear it all down, we'll miss you Zarf...", + Use: "destroy", + Aliases: []string{"d"}, + Short: "Tear it all down, we'll miss you Zarf...", Run: func(cmd *cobra.Command, args []string) { - burn() - _ = os.Remove(config.ZarfStatePath) - pattern := regexp.MustCompile(`(?mi)zarf-clean-.+\.sh$`) - scripts := utils.RecursiveFileList("/usr/local/bin", pattern) - // Iterate over al matching zarf-clean scripts and exec them - for _, script := range scripts { - // Run the matched script - _, _ = utils.ExecCommand(true, nil, script) - // Try to remove the script, but ignore any errors - _ = os.Remove(script) + state := k8s.LoadZarfState() + _ = os.Remove(".zarf-registry") + + if state.ZarfAppliance { + // If Zarf deployed the cluster, burn it all down + pattern := regexp.MustCompile(`(?mi)zarf-clean-.+\.sh$`) + scripts := utils.RecursiveFileList("/usr/local/bin", pattern) + // Iterate over al matching zarf-clean scripts and exec them + for _, script := range scripts { + // Run the matched script + _, _ = utils.ExecCommand(true, nil, script) + // Try to remove the script, but ignore any errors + _ = os.Remove(script) + } + } else { + // Perform chart uninstallation + helm.Destroy(removeComponents) + + // If Zarf didn't deploy the cluster, only delete the ZarfNamespace + k8s.DeleteZarfNamespace() } - burn() }, } -func burn() { - fmt.Println("") - for count := 0; count < 40; count++ { - fmt.Print("🔥") - } - fmt.Println("") -} - func init() { rootCmd.AddCommand(destroyCmd) destroyCmd.Flags().BoolVar(&confirmDestroy, "confirm", false, "Confirm the destroy action") + destroyCmd.Flags().BoolVar(&removeComponents, "remove-components", false, "Also remove any installed components outside the zarf namespace") _ = destroyCmd.MarkFlagRequired("confirm") } diff --git a/cli/cmd/initialize.go b/cli/cmd/initialize.go index 40a147930c..214218b237 100644 --- a/cli/cmd/initialize.go +++ b/cli/cmd/initialize.go @@ -1,176 +1,38 @@ package cmd import ( - "net" + "fmt" "os" - "path/filepath" "github.com/defenseunicorns/zarf/cli/config" "github.com/defenseunicorns/zarf/cli/internal/packager" - "github.com/defenseunicorns/zarf/cli/internal/pki" - "github.com/defenseunicorns/zarf/cli/internal/utils" - - "github.com/AlecAivazis/survey/v2" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -const invalidHostMessage = "The hostname provided (%v) was not a valid hostname. The hostname can only contain: 'a-z', 'A-Z', '0-9', '-', and '.' characters as defined by RFC-1035. If using localhost, you must use the 127.0.0.1.\n" - -var initOptions = packager.InstallOptions{} -var state = config.ZarfState{ - Kind: "ZarfState", -} - // initCmd represents the init command var initCmd = &cobra.Command{ - Use: "init", - Short: "Deploys the gitops service or appliance cluster on a clean linux box", - Long: "Flags are only required if running via automation, otherwise the init command will prompt you for your configuration choices", + Use: "init", + Aliases: []string{"i"}, + Short: "Deploys the gitops service or appliance cluster on a clean linux box", + Long: "Flags are only required if running via automation, otherwise the init command will prompt you for your configuration choices", Run: func(cmd *cobra.Command, args []string) { + zarfLogo := getLogo() + _, _ = fmt.Fprintln(os.Stderr, zarfLogo) - if !initOptions.Confirmed { - var confirm bool - prompt := &survey.Confirm{ - Message: "⚠️ This will initialize a new Zarf deployment on this machine which will make changes to your filesystem. You should not run zarf init more than once without first running zarf destroy. Do you want to continue?", - } - _ = survey.AskOne(prompt, &confirm) - if !confirm { - // Gracefully exit because they didn't want to play after all :-/ - os.Exit(0) - } - } + // Continue running package deploy for all components like any other package + config.DeployOptions.PackagePath = config.PackageInitName - handleTLSOptions() - pki.HandlePKI() - packager.Install(&initOptions) + // Run everything + packager.Deploy() }, } -// Check for cert paths provided via automation (both required) -func hasCertPaths() bool { - return state.TLS.CertPrivatePath != "" && state.TLS.CertPublicPath != "" -} - -// Ask user if they will be importing or generating certs, return true if importing certs -func promptIsImportCerts() bool { - var mode int - - if hasCertPaths() { - return true - } - - if initOptions.Confirmed { - // Assume generate on confirmed without cert paths - return false - } - - // Determine flow for generate or import - modePrompt := &survey.Select{ - Message: "Will Zarf be generating a TLS chain or importing an existing ingress cert?", - Options: []string{ - "Generate TLS chain with an ephemeral CA", - "Import user-provided cert keypair", - }, - } - _ = survey.AskOne(modePrompt, &mode) - - return mode == 1 -} - -// Ask user for the public and private key paths to import into the cluster -func promptCertPaths() { - prompt := &survey.Input{ - Message: "Enter a file path to the ingress public key", - Suggest: func(toComplete string) []string { - // Give some suggestions to users - files, _ := filepath.Glob(toComplete + "*") - return files - }, - } - _ = survey.AskOne(prompt, &state.TLS.CertPublicPath, survey.WithValidator(survey.Required)) - - prompt.Message = "Enter a file path to the ingress private key" - _ = survey.AskOne(prompt, &state.TLS.CertPrivatePath, survey.WithValidator(survey.Required)) -} - -// Ask user for the hostname or ip if not provided via automation and validate the input -func promptAndValidateHost() { - if state.TLS.Host == "" { - if initOptions.Confirmed { - // Fail if host is not provided on confirm - logrus.Fatalf(invalidHostMessage, state.TLS.Host) - } - - // If not provided, always ask for a host entry to avoid having to guess which entry in a cert if provided - prompt := &survey.Input{ - Message: "Enter a host DNS entry or IP Address for the cluster ingress. If using localhost, use 127.0.0.1", - Suggest: func(toComplete string) []string { - var suggestions []string - // Create a list of IPs to add to the suggestion box - interfaces, err := net.InterfaceAddrs() - if err == nil { - for _, iface := range interfaces { - // Conver the CIRD to the IP string if valid - ip, _, _ := net.ParseCIDR(iface.String()) - if utils.ValidHostname(ip.String()) { - suggestions = append(suggestions, ip.String()) - } - } - } - // Add the localhost hostname as well - hostname, _ := os.Hostname() - if hostname != "" { - suggestions = append(suggestions, hostname) - } - - return suggestions - }, - } - err := survey.AskOne(prompt, &state.TLS.Host, survey.WithValidator(survey.Required)) - if err != nil && err.Error() == os.Interrupt.String() { - // Handle CTRL+C - os.Exit(0) - } - } - - if !utils.ValidHostname(state.TLS.Host) { - // When hitting an invalid hostname... - if initOptions.Confirmed { - // ...if using automation end it all - logrus.Fatalf(invalidHostMessage, state.TLS.Host) - } - // ...otherwise, warn user, reset the field, and cycle the function - logrus.Warnf(invalidHostMessage, state.TLS.Host) - state.TLS.Host = "" - promptAndValidateHost() - } -} - -func handleTLSOptions() { - - // Get and validate host - promptAndValidateHost() - - // Get the cert path if this is an import - if promptIsImportCerts() && !hasCertPaths() { - promptCertPaths() - } - - // Persist the config the ZarfState - if err := config.WriteState(state); err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to save the zarf state file.") - } -} - func init() { - rootCmd.AddCommand(initCmd) - initCmd.Flags().BoolVar(&initOptions.Confirmed, "confirm", false, "Confirm the install without prompting") - initCmd.Flags().StringVar(&state.TLS.Host, "host", "", "Specify the host or IP for the gitops service ingress. E.g. host=10.10.10.5 or host=gitops.domain.com") - initCmd.Flags().StringVar(&state.TLS.CertPublicPath, "server-crt", "", "Path to the server public key if not generating unique PKI") - initCmd.Flags().StringVar(&state.TLS.CertPrivatePath, "server-key", "", "Path to the server private key if not generating unique PKI") - initCmd.Flags().StringVar(&initOptions.Components, "components", "", "Comma-separated list of components to install. Adding this flag will skip the init prompts for which components to install") + initCmd.Flags().BoolVar(&config.DeployOptions.Confirm, "confirm", false, "Confirm the install without prompting") + initCmd.Flags().StringVar(&config.TLS.Host, "host", "", "Specify the host or IP for the gitops service ingress. E.g. host=10.10.10.5 or host=gitops.domain.com") + initCmd.Flags().StringVar(&config.TLS.CertPublicPath, "server-crt", "", "Path to the server public key if not generating unique PKI") + initCmd.Flags().StringVar(&config.TLS.CertPrivatePath, "server-key", "", "Path to the server private key if not generating unique PKI") + initCmd.Flags().StringVar(&config.DeployOptions.Components, "components", "", "Comma-separated list of components to install. Adding this flag will skip the init prompts for which components to install") } diff --git a/cli/cmd/package.go b/cli/cmd/package.go index 7f8bec8193..b4d865a36d 100644 --- a/cli/cmd/package.go +++ b/cli/cmd/package.go @@ -9,40 +9,43 @@ import ( "github.com/spf13/cobra" ) -var confirmCreate bool -var confirmDeploy bool -var deployComponents string var insecureDeploy bool var shasum string var packageCmd = &cobra.Command{ - Use: "package", - Short: "Pack and unpack updates for the Zarf gitops service.", + Use: "package", + Aliases: []string{"p"}, + Short: "Pack and unpack updates for the Zarf gitops service.", } var packageCreateCmd = &cobra.Command{ - Use: "create", - Short: "Create an update package to push to the gitops server (runs online)", + Use: "create", + Aliases: []string{"c"}, + Short: "Create an update package to push to the gitops server (runs online)", Run: func(cmd *cobra.Command, args []string) { - packager.Create(confirmCreate) + packager.Create() }, } var packageDeployCmd = &cobra.Command{ - Use: "deploy PACKAGE", - Short: "Deploys an update package from a local file or URL (runs offline)", - Args: cobra.MaximumNArgs(1), + Use: "deploy [PACKAGE]", + Aliases: []string{"d"}, + Short: "Deploys an update package from a local file or URL (runs offline)", + Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { + var done func() packageName := choosePackage(args) - localPackagePath := packager.HandleIfURL(packageName, shasum, insecureDeploy) - packager.Deploy(localPackagePath, confirmDeploy, deployComponents) + config.DeployOptions.PackagePath, done = packager.HandleIfURL(packageName, shasum, insecureDeploy) + defer done() + packager.Deploy() }, } var packageInspectCmd = &cobra.Command{ - Use: "inspect PACKAGE", - Short: "lists the payload of an update package file (runs offline)", - Args: cobra.MaximumNArgs(1), + Use: "inspect [PACKAGE]", + Aliases: []string{"i"}, + Short: "lists the payload of an update package file (runs offline)", + Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { packageName := choosePackage(args) packager.Inspect(packageName) @@ -71,9 +74,9 @@ func init() { packageCmd.AddCommand(packageDeployCmd) packageCmd.AddCommand(packageInspectCmd) - packageCreateCmd.Flags().BoolVar(&confirmCreate, "confirm", false, "Confirm package creation without prompting") - packageDeployCmd.Flags().BoolVar(&confirmDeploy, "confirm", false, "Confirm package deployment without prompting") - packageDeployCmd.Flags().StringVar(&deployComponents, "components", "", "Comma-separated list of components to install. Adding this flag will skip the init prompts for which components to install") + packageCreateCmd.Flags().BoolVar(&config.DeployOptions.Confirm, "confirm", false, "Confirm package creation without prompting") + packageDeployCmd.Flags().BoolVar(&config.DeployOptions.Confirm, "confirm", false, "Confirm package deployment without prompting") + packageDeployCmd.Flags().StringVar(&config.DeployOptions.Components, "components", "", "Comma-separated list of components to install. Adding this flag will skip the init prompts for which components to install") packageDeployCmd.Flags().BoolVar(&insecureDeploy, "insecure", false, "Skip shasum validation of remote package. Required if deploying a remote package and `--shasum` is not provided") packageDeployCmd.Flags().StringVar(&shasum, "shasum", "", "Shasum of the package to deploy. Required if deploying a remote package and `--insecure` is not provided") } diff --git a/cli/cmd/pki.go b/cli/cmd/pki.go index 0b8018898d..32176bacd4 100644 --- a/cli/cmd/pki.go +++ b/cli/cmd/pki.go @@ -3,14 +3,13 @@ package cmd import ( "github.com/AlecAivazis/survey/v2" "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/internal/message/tls" "github.com/defenseunicorns/zarf/cli/internal/pki" "github.com/defenseunicorns/zarf/cli/internal/utils" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -var tempState config.ZarfState - var pkiCmd = &cobra.Command{ Use: "pki", Short: "PKI-related commands", @@ -21,23 +20,19 @@ var pkiRegenerate = &cobra.Command{ Short: "Regenerate the pki certs for the cluster ingress", Run: func(cmd *cobra.Command, args []string) { // Prompt for a hostname if it wasn't provided as a command flag - if tempState.TLS.Host == "" { + if config.TLS.Host == "" { prompt := &survey.Input{ - Message: "Enter a host DNS entry or IP Address for the gitops service ingress. If using localhost, use 127.0.0.1", + Message: "Enter a host DNS entry or IP Address for the gitops service ingress. If using localhost, use " + config.IPV4Localhost, } - _ = survey.AskOne(prompt, &tempState.TLS.Host, survey.WithValidator(survey.Required)) + _ = survey.AskOne(prompt, &config.TLS.Host, survey.WithValidator(survey.Required)) } // Verify the hostname provided is valid - if !utils.ValidHostname(tempState.TLS.Host) { - logrus.Fatalf(invalidHostMessage, tempState.TLS.Host) + if !utils.ValidHostname(config.TLS.Host) { + message.Fatalf(nil, tls.InvalidHostMessage, config.TLS.Host) } pki.GeneratePKI() - if err := config.WriteState(state); err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to save the zarf state file.") - } }, } @@ -46,10 +41,6 @@ var pkiImport = &cobra.Command{ Short: "Import an existing key pair for the cluster ingress", Run: func(cmd *cobra.Command, args []string) { pki.HandlePKI() - if err := config.WriteState(state); err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to save the zarf state file.") - } }, } @@ -58,8 +49,7 @@ func init() { pkiCmd.AddCommand(pkiRegenerate) pkiCmd.AddCommand(pkiImport) - pkiRegenerate.Flags().StringVar(&tempState.TLS.Host, "host", "", "Specify the host or IP for the gitops service ingress") - - pkiImport.Flags().StringVar(&tempState.TLS.CertPublicPath, "server-crt", "", "Path to the server public key if not generating unique PKI") - pkiImport.Flags().StringVar(&tempState.TLS.CertPrivatePath, "server-key", "", "Path to the server private key if not generating unique PKI") + pkiRegenerate.Flags().StringVar(&config.TLS.Host, "host", "", "Specify the host or IP for the gitops service ingress") + pkiImport.Flags().StringVar(&config.TLS.CertPublicPath, "server-crt", "", "Path to the server public key if not generating unique PKI") + pkiImport.Flags().StringVar(&config.TLS.CertPrivatePath, "server-key", "", "Path to the server private key if not generating unique PKI") } diff --git a/cli/cmd/prepare.go b/cli/cmd/prepare.go index 96a798a927..a89734df9d 100644 --- a/cli/cmd/prepare.go +++ b/cli/cmd/prepare.go @@ -2,22 +2,24 @@ package cmd import ( "fmt" + "github.com/defenseunicorns/zarf/cli/internal/packager" "io/ioutil" "github.com/AlecAivazis/survey/v2" "github.com/defenseunicorns/zarf/cli/internal/git" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/defenseunicorns/zarf/cli/internal/utils" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) +var repoHelmChartPath string var prepareCmd = &cobra.Command{ Use: "prepare", Short: "Tools to help prepare assets for packaging", } var prepareTransformGitLinks = &cobra.Command{ - Use: "patch-git HOST FILE", + Use: "patch-git [HOST] [FILE]", Short: "Converts all .git URLs to the specified Zarf HOST and with the Zarf URL pattern in a given FILE", Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { @@ -26,7 +28,7 @@ var prepareTransformGitLinks = &cobra.Command{ // Read the contents of the given file content, err := ioutil.ReadFile(fileName) if err != nil { - logrus.Fatal(err) + message.Fatalf(err, "Unable to read the file %s", fileName) } // Perform git url transformation via regex @@ -44,8 +46,7 @@ var prepareTransformGitLinks = &cobra.Command{ // Overwrite the file err = ioutil.WriteFile(fileName, []byte(processedText), 0640) if err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to write the changes back to the file") + message.Fatal(err, "Unable to write the changes back to the file") } } @@ -53,23 +54,35 @@ var prepareTransformGitLinks = &cobra.Command{ } var prepareComputeFileSha256sum = &cobra.Command{ - Use: "sha256sum FILE|URL", + Use: "sha256sum [FILE|URL]", Short: "Generate a SHA256SUM for the given file", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { fileName := args[0] hash, err := utils.GetSha256Sum(fileName) if err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to compute the hash") + message.Fatal(err, "Unable to compute the hash") } else { fmt.Println(hash) } }, } +var prepareFindImages = &cobra.Command{ + Use: "find-images", + Aliases: []string{"prep"}, + Short: "evaluates components in a zarf file to identify images specified in their helm charts and manifests", + Run: func(cmd *cobra.Command, args []string) { + packager.FindImages(repoHelmChartPath) + }, +} + func init() { rootCmd.AddCommand(prepareCmd) prepareCmd.AddCommand(prepareTransformGitLinks) prepareCmd.AddCommand(prepareComputeFileSha256sum) + prepareCmd.AddCommand(prepareFindImages) + + prepareFindImages.Flags().StringVarP(&repoHelmChartPath, "repo-chart-path", "p", "", `If git repos hold helm charts, often found with gitops tools, specify the chart path, e.g. "/" or "/chart"`) + } diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 4edf70fbdb..9e08963571 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -5,32 +5,35 @@ import ( "os" "strings" + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/defenseunicorns/zarf/cli/internal/packager" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) var zarfLogLevel = "" +var arch string var rootCmd = &cobra.Command{ - Use: "zarf COMMAND|ZARF-PACKAGE|ZARF-YAML", + Use: "zarf [COMMAND]|[ZARF-PACKAGE]|[ZARF-YAML]", PersistentPreRun: func(cmd *cobra.Command, args []string) { - setLogLevel(zarfLogLevel) - if logrus.GetLevel() != logrus.InfoLevel { - fmt.Printf("The log level has been changed to: %s\n", logrus.GetLevel()) + if zarfLogLevel != "" { + setLogLevel(zarfLogLevel) } + config.CliArch = arch }, - Short: "Small tool to bundle dependencies with K3s for airgapped deployments", + Short: "Small tool to bundle dependencies with K3s for air-gaped deployments", Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { if len(args) > 0 { - if strings.Contains(args[0], "zarf-package-") { - packager.Deploy(args[0], confirmDeploy, "") + if strings.Contains(args[0], "zarf-package-") || strings.Contains(args[0], "zarf-init") { + config.DeployOptions.PackagePath = args[0] + packager.Deploy() return } if args[0] == "zarf.yaml" { - packager.Create(confirmCreate) + packager.Create() return } } @@ -39,31 +42,37 @@ var rootCmd = &cobra.Command{ } func Execute() { - zarfLogo := getLogo() - fmt.Fprintln(os.Stderr, zarfLogo) cobra.CheckErr(rootCmd.Execute()) } func init() { + // Store the original cobra help func + originalHelp := rootCmd.HelpFunc() + rootCmd.SetHelpFunc(func(c *cobra.Command, s []string) { + // Don't show the zarf logo constantly + zarfLogo := getLogo() + _, _ = fmt.Fprintln(os.Stderr, zarfLogo) + // Re-add the original help function + originalHelp(c, s) + }) + rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - rootCmd.PersistentFlags().StringVarP(&zarfLogLevel, "log-level", "l", "info", "Log level when running Zarf. Valid options are: debug, info, warn, error, fatal") + rootCmd.PersistentFlags().StringVarP(&zarfLogLevel, "log-level", "l", "", "Log level when running Zarf. Valid options are: warn, info, debug, trace") + rootCmd.PersistentFlags().StringVarP(&arch, "architecture", "a", "", "Architecture for OCI images") } func setLogLevel(logLevel string) { - switch logLevel { - case "debug": - logrus.SetLevel(logrus.DebugLevel) - case "info": - logrus.SetLevel(logrus.InfoLevel) - case "warn": - logrus.SetLevel(logrus.WarnLevel) - case "error": - logrus.SetLevel(logrus.ErrorLevel) - case "fatal": - logrus.SetLevel(logrus.FatalLevel) - case "panic": - logrus.SetLevel(logrus.PanicLevel) - default: - logrus.Fatalf("Unrecognized log level entry: %s", logLevel) + match := map[string]message.LogLevel{ + "warn": message.WarnLevel, + "info": message.InfoLevel, + "debug": message.DebugLevel, + "trace": message.TraceLevel, + } + + if lvl, ok := match[logLevel]; ok { + message.SetLogLevel(lvl) + message.Note("Log level set to " + logLevel) + } else { + message.Warn("invalid log level setting") } } diff --git a/cli/cmd/tools.go b/cli/cmd/tools.go index c194d9016e..76a4a2d8b0 100644 --- a/cli/cmd/tools.go +++ b/cli/cmd/tools.go @@ -2,101 +2,131 @@ package cmd import ( "encoding/json" - "fmt" - "github.com/alecthomas/jsonschema" + "os" + "github.com/defenseunicorns/zarf/cli/types" + + "github.com/alecthomas/jsonschema" "github.com/defenseunicorns/zarf/cli/config" - "github.com/defenseunicorns/zarf/cli/internal/git" + "github.com/defenseunicorns/zarf/cli/internal/k8s" + "github.com/defenseunicorns/zarf/cli/internal/message" + k9s "github.com/derailed/k9s/cmd" craneCmd "github.com/google/go-containerregistry/cmd/crane/cmd" "github.com/google/go-containerregistry/pkg/crane" - v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/mholt/archiver/v3" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) var toolsCmd = &cobra.Command{ - Use: "tools", - Short: "Collection of additional tools to make airgap easier", + Use: "tools", + Aliases: []string{"t"}, + Short: "Collection of additional tools to make airgap easier", } // destroyCmd represents the init command var archiverCmd = &cobra.Command{ - Use: "archiver", - Short: "Compress/Decompress tools", + Use: "archiver", + Aliases: []string{"a"}, + Short: "Compress/Decompress tools", } var archiverCompressCmd = &cobra.Command{ - Use: "compress SOURCES ARCHIVE", - Short: "Compress a collection of sources based off of the destination file extension", - Args: cobra.MinimumNArgs(2), + Use: "compress SOURCES ARCHIVE", + Aliases: []string{"c"}, + Short: "Compress a collection of sources based off of the destination file extension", + Args: cobra.MinimumNArgs(2), Run: func(cmd *cobra.Command, args []string) { sourceFiles, destinationArchive := args[:len(args)-1], args[len(args)-1] err := archiver.Archive(sourceFiles, destinationArchive) if err != nil { - logrus.Fatal(err) + message.Fatal(err, "Unable to perform compression") } }, } var archiverDecompressCmd = &cobra.Command{ - Use: "decompress ARCHIVE DESTINATION", - Short: "Decompress an archive to a specified location.", - Args: cobra.ExactArgs(2), + Use: "decompress ARCHIVE DESTINATION", + Aliases: []string{"d"}, + Short: "Decompress an archive to a specified location.", + Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { sourceArchive, destinationPath := args[0], args[1] err := archiver.Unarchive(sourceArchive, destinationPath) if err != nil { - logrus.Fatal(err) + message.Fatal(err, "Unable to perform decompression") } }, } var registryCmd = &cobra.Command{ - Use: "registry", - Short: "Collection of registry commands provided by Crane", + Use: "registry", + Aliases: []string{"r"}, + Short: "Collection of registry commands provided by Crane", } var readCredsCmd = &cobra.Command{ Use: "get-admin-password", - Short: "Returns the Zarf admin password read from ~/.git-credentials", + Short: "Returns the Zarf admin password for gitea read from the zarf-state secret in the zarf namespace", Run: func(cmd *cobra.Command, args []string) { - authInfo := git.FindAuthForHost(config.GetTargetEndpoint()) - fmt.Println(authInfo.Auth.Password) + state := k8s.LoadZarfState() + if state.Distro == k8s.DistroIsUnknown { + // If no distro the zarf secret did not load properly + message.Fatalf(nil, "Unable to load the zarf/zarf-state secret, did you remember to run zarf init first?") + } + + // Continue loading state data if it is valid + config.InitState(state) + + fmt.Println(config.GetSecret(config.StateGitPush)) }, } var configSchemaCmd = &cobra.Command{ - Use: "config-schema", - Short: "Generates a JSON schema for the zarf.yaml configuration", + Use: "config-schema", + Aliases: []string{"c"}, + Short: "Generates a JSON schema for the zarf.yaml configuration", Run: func(cmd *cobra.Command, args []string) { - schema := jsonschema.Reflect(&config.ZarfPackage{}) + schema := jsonschema.Reflect(&types.ZarfPackage{}) output, err := json.MarshalIndent(schema, "", " ") if err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to generate the zarf config schema") + message.Fatal(err, "Unable to generate the zarf config schema") } fmt.Print(string(output)) }, } +var k9sCmd = &cobra.Command{ + Use: "monitor", + Aliases: []string{"m", "k9s"}, + Short: "Launch K9s tool for managing K8s clusters", + Run: func(cmd *cobra.Command, args []string) { + // Hack to make k9s think it's all alone + os.Args = []string{os.Args[0], "-n", "zarf"} + k9s.Execute() + }, +} + func init() { rootCmd.AddCommand(toolsCmd) toolsCmd.AddCommand(archiverCmd) toolsCmd.AddCommand(readCredsCmd) toolsCmd.AddCommand(configSchemaCmd) + toolsCmd.AddCommand(k9sCmd) + toolsCmd.AddCommand(registryCmd) + archiverCmd.AddCommand(archiverCompressCmd) archiverCmd.AddCommand(archiverDecompressCmd) - toolsCmd.AddCommand(registryCmd) - cranePlatformOptions := []crane.Option{ - crane.WithPlatform(&v1.Platform{OS: "linux", Architecture: "amd64"}), - } + // Ensure the arch is set to avoid crane nil pointer + config.SetAcrch() + + cranePlatformOptions := []crane.Option{config.ActiveCranePlatform} registryCmd.AddCommand(craneCmd.NewCmdAuthLogin()) registryCmd.AddCommand(craneCmd.NewCmdPull(&cranePlatformOptions)) registryCmd.AddCommand(craneCmd.NewCmdPush(&cranePlatformOptions)) registryCmd.AddCommand(craneCmd.NewCmdCopy(&cranePlatformOptions)) registryCmd.AddCommand(craneCmd.NewCmdCatalog(&cranePlatformOptions)) + } diff --git a/cli/cmd/version.go b/cli/cmd/version.go index 0bbf70fef0..389d76d96e 100644 --- a/cli/cmd/version.go +++ b/cli/cmd/version.go @@ -8,8 +8,9 @@ import ( ) var versionCmd = &cobra.Command{ - Use: "version", - Short: "Displays the version the zarf binary was built from", + Use: "version", + Aliases: []string{"v"}, + Short: "Displays the version the zarf binary was built from", Run: func(cmd *cobra.Command, args []string) { fmt.Println(config.CLIVersion) }, diff --git a/cli/config/config.go b/cli/config/config.go index 8acbdcc469..763067c4f1 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -1,38 +1,100 @@ package config import ( + "fmt" "os" "os/user" + "runtime" "strings" "time" + "github.com/defenseunicorns/zarf/cli/types" + + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/defenseunicorns/zarf/cli/internal/utils" - "github.com/sirupsen/logrus" + "github.com/google/go-containerregistry/pkg/crane" + v1 "github.com/google/go-containerregistry/pkg/v1" ) -const K3sBinary = "/usr/local/bin/k3s" -const K3sChartPath = "/var/lib/rancher/k3s/server/static/charts" -const K3sManifestPath = "/var/lib/rancher/k3s/server/manifests" -const K3sImagePath = "/var/lib/rancher/k3s/agent/images" -const PackageInitName = "zarf-init.tar.zst" -const PackagePrefix = "zarf-package-" -const ZarfGitUser = "zarf-git-user" -const ZarfStatePath = ".zarf-state.yaml" - -var CLIVersion = "unset" -var config ZarfPackage -var state ZarfState - -func init() { - if err := utils.ReadYaml(ZarfStatePath, &state); err != nil { - state.Kind = "ZarfState" - } -} +const ( + IPV4Localhost = "127.0.0.1" + + PackageInitName = "zarf-init.tar.zst" + PackagePrefix = "zarf-package-" + + // ZarfMaxChartNameLength limits helm chart name size to account for K8s/helm limits and zarf prefix + ZarfMaxChartNameLength = 40 + ZarfGitPushUser = "zarf-git-user" + ZarfRegistryPushUser = "zarf-push" + ZarfRegistryPullUser = "zarf-pull" + ZarfSeedPort = "45000" + ZarfRegistry = IPV4Localhost + ":45001" + ZarfLocalSeedRegistry = IPV4Localhost + ":" + ZarfSeedPort + + ZarfSeedTypeCLIInject = "cli-inject" + ZarfSeedTypeRuntimeRegistry = "runtime-registry" + ZarfSeedTypeInClusterRegistry = "in-cluster-registry" + + ZarfConnectLabelName = "zarf.dev/connect-name" + ZarfConnectAnnotationDescription = "zarf.dev/connect-description" + ZarfConnectAnnotationUrl = "zarf.dev/connect-url" +) + +var ( + // CLIVersion track the version of the CLI + CLIVersion = "unset" + + // TLS options used for cert creation + TLS types.TLSConfig + + // DeployOptions tracks user-defined values for the active deployment + DeployOptions types.ZarfDeployOptions + + ActiveCranePlatform crane.Option + + CliArch string + + // Private vars + config types.ZarfPackage + state types.ZarfState +) func IsZarfInitConfig() bool { + message.Debug("config.IsZarfInitConfig") return strings.ToLower(config.Kind) == "zarfinitconfig" } +func SetAcrch() { + var arch string + if CliArch == "" { + // If not cli override for arch, set to the package arch + arch = config.Metadata.Architecture + + if arch == "" { + // Finally, default to current system arch when all else fails + arch = runtime.GOARCH + } + } else { + arch = CliArch + } + + message.Debugf("config.SetArch(%s)", arch) + config.Build.Architecture = arch + // Use the arch to define the image push/pull options for crane + ActiveCranePlatform = crane.WithPlatform(&v1.Platform{OS: "linux", Architecture: arch}) +} + +// GetSeedImages returns a list of image strings specified in the package, but only for init packages +func GetSeedImages() []string { + message.Debugf("config.GetSeedImages()") + // Only allow seed images for init config + if IsZarfInitConfig() { + return config.Seed + } else { + return []string{} + } +} + func GetPackageName() string { metadata := GetMetaData() if metadata.Uncompressed { @@ -42,19 +104,19 @@ func GetPackageName() string { } } -func GetDataInjections() []ZarfData { +func GetDataInjections() []types.ZarfData { return config.Data } -func GetMetaData() ZarfMetadata { +func GetMetaData() types.ZarfMetadata { return config.Metadata } -func GetComponents() []ZarfComponent { +func GetComponents() []types.ZarfComponent { return config.Components } -func GetBuildData() ZarfBuildData { +func GetBuildData() types.ZarfBuildData { return config.Build } @@ -62,18 +124,26 @@ func GetValidPackageExtensions() [3]string { return [...]string{".tar.zst", ".tar", ".zip"} } -func GetState() ZarfState { +func InitState(tmpState types.ZarfState) { + message.Debugf("config.InitState(%v)", tmpState) + state = tmpState + initSecrets() +} + +func GetState() types.ZarfState { return state } -func GetTargetEndpoint() string { - return state.TLS.Host +func GetRegistry() string { + return fmt.Sprintf("%s:%s", IPV4Localhost, state.Registry.NodePort) } -func WriteState(incomingState ZarfState) error { - logrus.Debug(incomingState) - state = incomingState - return utils.WriteYaml(ZarfStatePath, state, 0600) +func GetSeedRegistry() string { + if state.Registry.SeedType == ZarfSeedTypeCLIInject { + return "docker.io" + } else { + return fmt.Sprintf("%s:%s", TLS.Host, ZarfSeedPort) + } } func LoadConfig(path string) error { @@ -81,6 +151,7 @@ func LoadConfig(path string) error { } func BuildConfig(path string) error { + message.Debugf("config.BuildConfig(%v)", path) now := time.Now() currentUser, userErr := user.Current() hostname, hostErr := os.Hostname() diff --git a/cli/config/secret.go b/cli/config/secret.go new file mode 100644 index 0000000000..b978fcde2a --- /dev/null +++ b/cli/config/secret.go @@ -0,0 +1,74 @@ +package config + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + + "github.com/defenseunicorns/zarf/cli/internal/message" +) + +type SecretSelector = string + +type SecretMap struct { + length int + computed string + valid bool +} + +const ( + StateRegistryPush SecretSelector = "registry-push" + StateRegistryPull SecretSelector = "registry-pull" + StateRegistrySecret SecretSelector = "registry-secret" + StateGitPush SecretSelector = "git-push" + StateGitPull SecretSelector = "git-pull" + StateLogging SecretSelector = "logging" +) + +var selectors = map[SecretSelector]SecretMap{ + StateRegistryPush: {length: 48}, + StateRegistryPull: {length: 48}, + StateRegistrySecret: {length: 48}, + StateGitPush: {length: 24}, + StateGitPull: {length: 24}, + StateLogging: {length: 24}, +} + +func GetSecret(selector SecretSelector) string { + message.Debugf("config.GetSecret(%v)", selector) + if match, ok := selectors[selector]; ok { + return match.computed + } + return "" +} + +func initSecrets() { + message.Debug("config.initSecrets()") + for filter, selector := range selectors { + output, err := loadSecret(filter, selector.length) + if err != nil { + message.Debug(err) + } else { + selector.valid = true + selector.computed = output + selectors[filter] = selector + } + } +} + +func loadSecret(filter SecretSelector, length int) (string, error) { + message.Debugf("config.loadSecret(%v, %v)", filter, length) + if state.Secret == "" { + return "", fmt.Errorf("invalid root secret in the ZarfState") + } + hash := sha256.New() + text := fmt.Sprintf("%s:%s", filter, state.Secret) + hash.Write([]byte(text)) + output := hex.EncodeToString(hash.Sum(nil))[:length] + + if output != "" { + return output, nil + } else { + return "", fmt.Errorf("unable to generate secret for %s", filter) + } +} diff --git a/cli/config/types.go b/cli/config/types.go deleted file mode 100644 index 6c172d5d5e..0000000000 --- a/cli/config/types.go +++ /dev/null @@ -1,78 +0,0 @@ -package config - -type ZarfFile struct { - Source string `yaml:"source"` - Shasum string `yaml:"shasum,omitempty"` - Target string `yaml:"target"` - Executable bool `yaml:"executable,omitempty"` - Symlinks []string `yaml:"symlinks,omitempty"` - Template bool `yaml:"template,omitempty"` -} - -type ZarfChart struct { - Name string `yaml:"name"` - Url string `yaml:"url"` - Version string `yaml:"version"` -} - -type ZarfComponent struct { - Name string `yaml:"name"` - Description string `yaml:"description,omitempty"` - Default bool `yaml:"default,omitempty"` - Required bool `yaml:"required,omitempty"` - Files []ZarfFile `yaml:"files,omitempty"` - ManifestsPath string `yaml:"manifests,omitempty"` - Images []string `yaml:"images,omitempty"` - Charts []ZarfChart `yaml:"charts,omitempty"` - Repos []string `yaml:"repos,omitempty"` - Scripts ZarfComponentScripts `yaml:"scripts,omitempty"` -} - -type ZarfComponentScripts struct { - Retry bool `yaml:"retry,omitempty"` - Before []string `yaml:"before,omitempty"` - After []string `yaml:"after,omitempty"` -} - -type ZarfMetadata struct { - Name string `yaml:"name,omitempty"` - Description string `yaml:"description,omitempty"` - Version string `yaml:"version,omitempty"` - Uncompressed bool `yaml:"uncompressed,omitempty"` -} - -type ZarfContainerTarget struct { - Namespace string `yaml:"namespace"` - Selector string `yaml:"selector"` - Container string `yaml:"container,omitempty"` - Path string `yaml:"path"` -} - -type ZarfData struct { - Source string `yaml:"source"` - Target ZarfContainerTarget `yaml:"target"` -} - -type ZarfBuildData struct { - Terminal string `yaml:"terminal"` - User string `yaml:"user"` - Timestamp string `yaml:"timestamp"` - Version string `yaml:"string"` -} - -type ZarfPackage struct { - Kind string `yaml:"kind,omitempty"` - Metadata ZarfMetadata `yaml:"metadata,omitempty"` - Build ZarfBuildData `yaml:"build,omitempty"` - Data []ZarfData `yaml:"data,omitempty"` - Components []ZarfComponent `yaml:"components,omitempty"` -} - -type ZarfState struct { - Kind string `yaml:"kind"` - TLS struct { - CertPublicPath string `yaml:"certPublicPath"` - CertPrivatePath string `yaml:"certPrivatePath"` - Host string `yaml:"host"` - } `yaml:"tls"` -} diff --git a/cli/internal/git/checkout.go b/cli/internal/git/checkout.go index 0bfd597116..3fe9ab0294 100644 --- a/cli/internal/git/checkout.go +++ b/cli/internal/git/checkout.go @@ -1,10 +1,10 @@ package git import ( + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" - "github.com/sirupsen/logrus" ) // CheckoutTag performs a `git checkout` of the provided tag to a detached HEAD @@ -16,25 +16,18 @@ func CheckoutTag(path string, tag string) { } // CheckoutTagAsBranch performs a `git checkout` of the provided tag but rather -// than checking out to a detatched head, checks out to the provided branch ref +// than checking out to a detached head, checks out to the provided branch ref // It will delete the branch provided if it exists func CheckoutTagAsBranch(path string, tag string, branch plumbing.ReferenceName) { - logContext := logrus.WithFields(logrus.Fields{ - "Path": path, - "Tag": tag, - "Branch": branch.String(), - }) - + message.Debugf("Checkout tag %s as branch %s for %s", tag, branch.String(), path) repo, err := git.PlainOpen(path) if err != nil { - logContext.Debug(err) - logContext.Fatal("Not a valid git repo or unable to open") + message.Fatal(err, "Not a valid git repo or unable to open") } tagRef, err := repo.Tag(tag) if err != nil { - logContext.Debug(err) - logContext.Fatal("Failed to locate tag in repository.") + message.Fatal(err, "Failed to locate tag in repository.") } checkoutHashAsBranch(path, tagRef.Hash(), branch) } @@ -43,24 +36,18 @@ func CheckoutTagAsBranch(path string, tag string, branch plumbing.ReferenceName) // with the provided hash // It will delete the branch provided if it exists func checkoutHashAsBranch(path string, hash plumbing.Hash, branch plumbing.ReferenceName) { - logContext := logrus.WithFields(logrus.Fields{ - "Path": path, - "Hash": hash.String(), - "Branch": branch.String(), - }) + message.Debugf("Checkout hash %s as branch %s for %s", hash.String(), branch.String(), path) - DeleteBranchIfExists(path, branch) + _ = deleteBranchIfExists(path, branch) repo, err := git.PlainOpen(path) if err != nil { - logContext.Debug(err) - logContext.Fatal("Not a valid git repo or unable to open") + message.Fatal(err, "Not a valid git repo or unable to open") } objRef, err := repo.Object(plumbing.AnyObject, hash) if err != nil { - logContext.Debug(err) - logContext.Fatal("An error occurred when getting the repo's object reference") + message.Fatal(err, "An error occurred when getting the repo's object reference") } var commitHash plumbing.Hash @@ -72,8 +59,7 @@ func checkoutHashAsBranch(path string, hash plumbing.Hash, branch plumbing.Refer default: // This shouldn't ever hit, but we should at least log it if someday it // does get hit - logContext.Debug("Unsupported tag hash type: " + objRef.Type().String()) - logContext.Fatal("Checkout failed. Hash type not supported.") + message.Fatalf(err, "Checkout failed. Hash type %s not supported.", objRef.Type().String()) } options := &git.CheckoutOptions{ @@ -87,28 +73,23 @@ func checkoutHashAsBranch(path string, hash plumbing.Hash, branch plumbing.Refer // checkout performs a `git checkout` on the path provided using the options provided // It assumes the caller knows what to do and does not perform any safety checks func checkout(path string, checkoutOptions *git.CheckoutOptions) { - logContext := logrus.WithFields(logrus.Fields{ - "Path": path, - }) + message.Debugf("Git checkout %s", path) // Open the given repo repo, err := git.PlainOpen(path) if err != nil { - logContext.Debug(err) - logContext.Fatal("Not a valid git repo or unable to open") + message.Fatal(err, "Not a valid git repo or unable to open") } // Get the working tree so we can change refs tree, err := repo.Worktree() if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to load the git repo") + message.Fatal(err, "Unable to load the git repo") } // Perform the checkout err = tree.Checkout(checkoutOptions) if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to perform checkout") + message.Fatal(err, "Unable to perform checkout") } } diff --git a/cli/internal/git/fetch.go b/cli/internal/git/fetch.go index 322c8207de..ec883506b2 100644 --- a/cli/internal/git/fetch.go +++ b/cli/internal/git/fetch.go @@ -3,44 +3,32 @@ package git import ( "path" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/go-git/go-git/v5" goConfig "github.com/go-git/go-git/v5/config" - "github.com/sirupsen/logrus" ) -// FetchTag performs a `git fetch` of _only_ the provided tag -func FetchTag(gitDirectory string, tag string) { - logContext := logrus.WithFields(logrus.Fields{ - // Base should be similar to the repo name - "Repo": path.Base(gitDirectory), - }) +// fetchTag performs a `git fetch` of _only_ the provided tag +func fetchTag(gitDirectory string, tag string) { + message.Debugf("Fetch git tag %s from repo %s", tag, path.Base(gitDirectory)) repo, err := git.PlainOpen(gitDirectory) if err != nil { - logContext.Fatal(err) + message.Fatal(err, "Unable to load the git repo") } remotes, err := repo.Remotes() // There should never be no remotes, but it's easier to account for than // let be a bug later if err != nil || len(remotes) == 0 { - if err != nil { - logContext.Debug(err) - } - logContext.Fatal("Failed to identify remotes.") + message.Fatal(err, "Failed to identify remotes.") } gitUrl := remotes[0].Config().URLs[0] - // Now that we have an exact match, we may as well update the logger, - // especially since nothing has been logged to this point that hasn't been - // fatal. - logContext = logrus.WithFields(logrus.Fields{ - "Remote": gitUrl, - }) + message.Debugf("Attempting to find tag: %s for %s", tag, gitUrl) gitCred := FindAuthForHost(gitUrl) - logContext.Debug("Attempting to find tag: " + tag) fetchOptions := &git.FetchOptions{ RemoteName: onlineRemoteName, RefSpecs: []goConfig.RefSpec{ @@ -55,11 +43,8 @@ func FetchTag(gitDirectory string, tag string) { err = repo.Fetch(fetchOptions) if err == git.ErrTagExists { - logContext.Info("Tag already fetched") + message.Debug("Tag already fetched") } else if err != nil { - logContext.Debug(err) - logContext.Fatal("Not a valid tag or unable to fetch") + message.Fatal(err, "Not a valid tag or unable to fetch") } - - logContext.Info("Git tag fetched") } diff --git a/cli/internal/git/pull.go b/cli/internal/git/pull.go index 15bd743abd..19589855c0 100644 --- a/cli/internal/git/pull.go +++ b/cli/internal/git/pull.go @@ -1,38 +1,33 @@ package git import ( - "os" - + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/defenseunicorns/zarf/cli/internal/utils" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" - "github.com/sirupsen/logrus" "strings" ) const onlineRemoteName = "online-upstream" -func DownloadRepoToTemp(gitUrl string) string { - path := utils.MakeTempDir() +func DownloadRepoToTemp(gitUrl string, spinner *message.Spinner) string { + path, _ := utils.MakeTempDir() // If downloading to temp, grab all tags since the repo isn't being - // packaged anyways and it saves us from having to fetch the tags + // packaged anyway, and it saves us from having to fetch the tags // later if we need them - pull(gitUrl, path) + pull(gitUrl, path, spinner) return path } -func Pull(gitUrl string, targetFolder string) string { +func Pull(gitUrl string, targetFolder string, spinner *message.Spinner) string { path := targetFolder + "/" + transformURLtoRepoName(gitUrl) - pull(gitUrl, path) + pull(gitUrl, path, spinner) return path } -func pull(gitUrl string, targetFolder string) { - logContext := logrus.WithFields(logrus.Fields{ - "Remote": gitUrl, - }) - logContext.Info("Processing git repo") +func pull(gitUrl string, targetFolder string, spinner *message.Spinner) { + spinner.Updatef("Processing git repo %s", gitUrl) gitCred := FindAuthForHost(gitUrl) @@ -40,7 +35,7 @@ func pull(gitUrl string, targetFolder string) { fetchAllTags := len(matches) == 1 cloneOptions := &git.CloneOptions{ URL: matches[0], - Progress: os.Stdout, + Progress: spinner, RemoteName: onlineRemoteName, } @@ -57,10 +52,9 @@ func pull(gitUrl string, targetFolder string) { repo, err := git.PlainClone(targetFolder, false, cloneOptions) if err == git.ErrRepositoryAlreadyExists { - logContext.Info("Repo already cloned") + spinner.Debugf("Repo already cloned") } else if err != nil { - logContext.Debug(err) - logContext.Fatal("Not a valid git repo or unable to clone") + spinner.Fatalf(err, "Not a valid git repo or unable to clone") } if !fetchAllTags { @@ -72,22 +66,20 @@ func pull(gitUrl string, targetFolder string) { if err != nil { // No repo head available - logContext.Debug(err) - logContext.Warn("Failed to identify repo head. Tag will be pushed to 'master'.") + spinner.Errorf(err, "Failed to identify repo head. Tag will be pushed to 'master'.") } else if head.Name().IsBranch() { // Valid repo head and it is a branch trunkBranchName = head.Name() } else { // Valid repo head but not a branch - logContext.Warn("No branch found for this repo head. Tag will be pushed to 'master'.") + spinner.Errorf(nil, "No branch found for this repo head. Tag will be pushed to 'master'.") } - RemoveLocalBranchRefs(targetFolder) - RemoveOnlineRemoteRefs(targetFolder) + _, _ = removeLocalBranchRefs(targetFolder) + _, _ = removeOnlineRemoteRefs(targetFolder) - FetchTag(targetFolder, tag) + fetchTag(targetFolder, tag) CheckoutTagAsBranch(targetFolder, tag, trunkBranchName) - } - logContext.Info("Git repo synced") + } } diff --git a/cli/internal/git/push.go b/cli/internal/git/push.go index 9b29f0c977..f16db5f8d7 100644 --- a/cli/internal/git/push.go +++ b/cli/internal/git/push.go @@ -1,44 +1,61 @@ package git import ( - "os" + "fmt" "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/k8s" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/defenseunicorns/zarf/cli/internal/utils" "github.com/go-git/go-git/v5" goConfig "github.com/go-git/go-git/v5/config" - "github.com/sirupsen/logrus" + "github.com/go-git/go-git/v5/plumbing/transport/http" ) const offlineRemoteName = "offline-downstream" const onlineRemoteRefPrefix = "refs/remotes/" + onlineRemoteName + "/" func PushAllDirectories(localPath string) { - paths := utils.ListDirectories(localPath) + // Establish a git tunnel to send the repos + tunnel := k8s.NewZarfTunnel() + tunnel.Connect(k8s.ZarfGit, false) + + paths, err := utils.ListDirectories(localPath) + if err != nil { + message.Fatalf(err, "unable to list the %s directory", localPath) + } + + spinner := message.NewProgressSpinner("Processing %d git repos", len(paths)) + defer spinner.Stop() + for _, path := range paths { - push(path) + spinner.Updatef("Pushing git repo %s", localPath) + if err := push(path, spinner); err != nil { + spinner.Fatalf(err, "Unable to push the git repo %s", localPath) + } } -} -func push(localPath string) { + spinner.Success() + tunnel.Close() +} - logContext := logrus.WithField("repo", localPath) - logContext.Info("Processing git repo") +func push(localPath string, spinner *message.Spinner) error { // Open the given repo repo, err := git.PlainOpen(localPath) if err != nil { - logContext.Fatal("Not a valid git repo or unable to open") + return fmt.Errorf("not a valid git repo or unable to open: %w", err) } // Get the upstream URL remote, err := repo.Remote(onlineRemoteName) if err != nil { - logContext.Warn("Unable to find the git remote") - return + return fmt.Errorf("unable to find the git remote: %w", err) + } remoteUrl := remote.Config().URLs[0] - targetUrl := transformURL("https://"+config.GetTargetEndpoint(), remoteUrl) + targetHost := fmt.Sprintf("http://%s:%d", config.IPV4Localhost, k8s.PortGit) + targetUrl := transformURL(targetHost, remoteUrl) _, err = repo.CreateRemote(&goConfig.RemoteConfig{ Name: offlineRemoteName, @@ -46,23 +63,26 @@ func push(localPath string) { }) if err != nil { - logContext.Debug(err) - logContext.Fatal("Failed to create offline remote") + return fmt.Errorf("failed to create offline remote: %w", err) } - gitCred := FindAuthForHost(config.GetTargetEndpoint()) - - pushContext := logContext.WithField("target", targetUrl) + gitCred := http.BasicAuth{ + Username: config.ZarfGitPushUser, + Password: config.GetSecret(config.StateGitPush), + } // Since we are pushing HEAD:refs/heads/master on deployment, leaving // duplicates of the HEAD ref (ex. refs/heads/master, // refs/remotes/online-upstream/master, will cause the push to fail) - removedRefs := RemoveHeadCopies(localPath) + removedRefs, err := removeHeadCopies(localPath) + if err != nil { + return fmt.Errorf("unable to remove unused git refs from the repo: %w", err) + } err = repo.Push(&git.PushOptions{ RemoteName: offlineRemoteName, - Auth: &gitCred.Auth, - Progress: os.Stdout, + Auth: &gitCred, + Progress: spinner, // If a provided refspec doesn't push anything, it is just ignored RefSpecs: []goConfig.RefSpec{ "refs/heads/*:refs/heads/*", @@ -72,15 +92,14 @@ func push(localPath string) { }) if err == git.NoErrAlreadyUpToDate { - pushContext.Info("Repo already up-to-date") + spinner.Debugf("Repo already up-to-date") } else if err != nil { - pushContext.Debug(err) - pushContext.Warn("Unable to push repo to the gitops service") - } else { - pushContext.Info("Repo updated") + return fmt.Errorf("unable to push repo to the gitops service: %w", err) } // Add back the refs we removed just incase this push isn't the last thing // being run and a later task needs to reference them. - AddRefs(localPath, removedRefs) + addRefs(localPath, removedRefs) + + return nil } diff --git a/cli/internal/git/utils.go b/cli/internal/git/utils.go index ce07bdb2ac..a2853e36a1 100644 --- a/cli/internal/git/utils.go +++ b/cli/internal/git/utils.go @@ -2,17 +2,16 @@ package git import ( "bufio" + "fmt" "net/url" "os" "regexp" "strings" - "github.com/defenseunicorns/zarf/cli/config" - "github.com/defenseunicorns/zarf/cli/internal/utils" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/sirupsen/logrus" ) type Credential struct { @@ -24,7 +23,7 @@ func MutateGitUrlsInText(host string, text string) string { extractPathRegex := regexp.MustCompilePOSIX(`https?://[^/]+/(.*\.git)`) output := extractPathRegex.ReplaceAllStringFunc(text, func(match string) string { if strings.Contains(match, "/zarf-git-user/") { - logrus.WithField("Match", match).Warn("This url seems to have been previously patched.") + message.Warnf("%s seems to have been previously patched.", match) return match } return transformURL(host, match) @@ -40,10 +39,7 @@ func transformURLtoRepoName(url string) string { func transformURL(baseUrl string, url string) string { replaced := transformURLtoRepoName(url) output := baseUrl + "/zarf-git-user/" + replaced - logrus.WithFields(logrus.Fields{ - "Old": url, - "New": output, - }).Info("Transformed Git URL") + message.Debugf("Rewrite git URL: %s -> %s", url, output) return output } @@ -57,7 +53,12 @@ func credentialParser() []Credential { var credentials []Credential credentialsFile, _ := os.Open(credentialsPath) - defer credentialsFile.Close() + defer func(credentialsFile *os.File) { + err := credentialsFile.Close() + if err != nil { + message.Debugf("Unable to load an existing git credentials file: %w", err) + } + }(credentialsFile) scanner := bufio.NewScanner(credentialsFile) for scanner.Scan() { @@ -98,71 +99,9 @@ func FindAuthForHost(baseUrl string) Credential { return matchedCred } -func GetOrCreateZarfSecret() string { - var gitSecret string - - credentials := FindAuthForHost(config.GetTargetEndpoint()) - - if (credentials == Credential{}) { - gitSecret = CredentialsGenerator() - } else { - gitSecret = credentials.Auth.Password - } - - return gitSecret -} - -func CredentialsGenerator() string { - - // Get a random secret for use in the cluster - gitSecret := utils.RandomString(28) - credentialsPath := credentialFilePath() - - // Prevent duplicates by purging the git creds file~ - _ = os.Remove(credentialsPath) - - credentialsFile, err := os.OpenFile(credentialsPath, os.O_CREATE|os.O_WRONLY, 0600) - if err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to access the git credentials file") - } - defer credentialsFile.Close() - - // Needed by zarf to do repo pushes - zarfUrl := url.URL{ - Scheme: "https", - User: url.UserPassword(config.ZarfGitUser, gitSecret), - Host: config.GetTargetEndpoint(), - } - - credentialsText := zarfUrl.String() + "\n" - - // Write the entry to the file - _, err = credentialsFile.WriteString(credentialsText) - if err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to update the git credentials file") - } - - // Save the change - err = credentialsFile.Sync() - if err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to update the git credentials file") - } - - return gitSecret -} - -// GetTaggedUrl builds a URL of the repo@tag format -// It returns a string of format repo@tag -func GetTaggedUrl(gitUrl string, gitTag string) string { - return gitUrl + "@" + gitTag -} - -// RemoveLocalBranchRefs removes all refs that are local branches +// removeLocalBranchRefs removes all refs that are local branches // It returns a slice of references deleted -func RemoveLocalBranchRefs(gitDirectory string) []*plumbing.Reference { +func removeLocalBranchRefs(gitDirectory string) ([]*plumbing.Reference, error) { return removeReferences( gitDirectory, func(ref *plumbing.Reference) bool { @@ -171,9 +110,9 @@ func RemoveLocalBranchRefs(gitDirectory string) []*plumbing.Reference { ) } -// RemoveOnlineRemoteRefs removes all refs pointing to the online-upstream +// removeOnlineRemoteRefs removes all refs pointing to the online-upstream // It returns a slice of references deleted -func RemoveOnlineRemoteRefs(gitDirectory string) []*plumbing.Reference { +func removeOnlineRemoteRefs(gitDirectory string) ([]*plumbing.Reference, error) { return removeReferences( gitDirectory, func(ref *plumbing.Reference) bool { @@ -182,20 +121,18 @@ func RemoveOnlineRemoteRefs(gitDirectory string) []*plumbing.Reference { ) } -// RemoveHeadCopies removes any refs that aren't HEAD but have the same hash +// removeHeadCopies removes any refs that aren't HEAD but have the same hash // It returns a slice of references deleted -func RemoveHeadCopies(gitDirectory string) []*plumbing.Reference { - logContext := logrus.WithField("Repo", gitDirectory) +func removeHeadCopies(gitDirectory string) ([]*plumbing.Reference, error) { + message.Debugf("Remove head copies for %s", gitDirectory) repo, err := git.PlainOpen(gitDirectory) if err != nil { - logContext.Debug(err) - logContext.Fatal("Not a valid git repo or unable to open") + return nil, fmt.Errorf("not a valid git repo or unable to open: %w", err) } head, err := repo.Head() if err != nil { - logContext.Debug(err) - logContext.Fatal("Failed to identify references when getting the repo's head") + return nil, fmt.Errorf("failed to identify references when getting the repo's head: %w", err) } headHash := head.Hash().String() @@ -214,27 +151,24 @@ func RemoveHeadCopies(gitDirectory string) []*plumbing.Reference { func removeReferences( gitDirectory string, shouldRemove func(*plumbing.Reference) bool, -) []*plumbing.Reference { - logContext := logrus.WithField("Repo", gitDirectory) +) ([]*plumbing.Reference, error) { + message.Debugf("Remove git references %s", gitDirectory) repo, err := git.PlainOpen(gitDirectory) if err != nil { - logContext.Debug(err) - logContext.Fatal("Not a valid git repo or unable to open") + return nil, fmt.Errorf("not a valid git repo or unable to open: %w", err) } references, err := repo.References() if err != nil { - logContext.Debug(err) - logContext.Fatal("Failed to identify references when getting the repo's references") + return nil, fmt.Errorf("failed to identify references when getting the repo's references: %w", err) } head, err := repo.Head() if err != nil { - logContext.Debug(err) - logContext.Fatal("Failed to identify head") + return nil, fmt.Errorf("failed to identify head: %w", err) } - removedRefs := []*plumbing.Reference{} + var removedRefs []*plumbing.Reference err = references.ForEach(func(ref *plumbing.Reference) error { refIsNotHeadOrHeadTarget := ref.Name() != plumbing.HEAD && ref.Name() != head.Name() // Run shouldRemove inline here to take advantage of short circuit @@ -250,58 +184,51 @@ func removeReferences( }) if err != nil { - logContext.Debug(err) - logContext.Fatal("Failed to remove references") + return nil, fmt.Errorf("failed to remove references: %w", err) } - return removedRefs + return removedRefs, nil } -// AddRefs adds a provided arbitrary list of references to a repo +// addRefs adds a provided arbitrary list of references to a repo // It is intended to be used with references returned by a Remove function -func AddRefs(gitDirectory string, refs []*plumbing.Reference) { - logContext := logrus.WithField("Repo", gitDirectory) +func addRefs(gitDirectory string, refs []*plumbing.Reference) error { + message.Debugf("Add git refs %s", gitDirectory) repo, err := git.PlainOpen(gitDirectory) if err != nil { - logContext.Debug(err) - logContext.Fatal("Not a valid git repo or unable to open") + return fmt.Errorf("not a valid git repo or unable to open: %w", err) } for _, ref := range refs { err = repo.Storer.SetReference(ref) if err != nil { - logContext.Debug(err) - logContext.Fatal("Failed to add references") + return fmt.Errorf("failed to add references: %w", err) } } + + return nil } -// DeleteBranchIfExists ensures the provided branch name does not exist -func DeleteBranchIfExists(gitDirectory string, branchName plumbing.ReferenceName) { - logContext := logrus.WithFields(logrus.Fields{ - "Repo": gitDirectory, - "Branch": branchName.String, - }) +// deleteBranchIfExists ensures the provided branch name does not exist +func deleteBranchIfExists(gitDirectory string, branchName plumbing.ReferenceName) error { + message.Debugf("Delete branch %s for %s if it exists", branchName.String(), gitDirectory) repo, err := git.PlainOpen(gitDirectory) if err != nil { - logContext.Debug(err) - logContext.Fatal("Not a valid git repo or unable to open") + return fmt.Errorf("not a valid git repo or unable to open: %w", err) } // Deletes the branch by name err = repo.DeleteBranch(branchName.Short()) if err != nil && err != git.ErrBranchNotFound { - logContext.Debug(err) - logContext.Fatal("Failed to delete branch") + return fmt.Errorf("failed to delete branch: %w", err) } // Delete reference too err = repo.Storer.RemoveReference(branchName) if err != nil && err != git.ErrInvalidReference { - logContext.Debug(err) - logContext.Fatal("Failed to delete branch reference") + return fmt.Errorf("failed to delete branch reference: %w", err) } - logContext.Info("Branch deleted") + return nil } diff --git a/cli/internal/helm/chart.go b/cli/internal/helm/chart.go new file mode 100644 index 0000000000..8ce3275543 --- /dev/null +++ b/cli/internal/helm/chart.go @@ -0,0 +1,311 @@ +package helm + +import ( + "fmt" + "io/ioutil" + "time" + + "github.com/defenseunicorns/zarf/cli/types" + + "github.com/defenseunicorns/zarf/cli/internal/message" + "helm.sh/helm/v3/pkg/action" + corev1 "k8s.io/api/core/v1" + + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/release" + "helm.sh/helm/v3/pkg/storage/driver" +) + +type ConnectString struct { + Description string + Url string +} +type ConnectStrings map[string]ConnectString +type ChartOptions struct { + BasePath string + Chart types.ZarfChart + ReleaseName string + ChartLoadOverride string + ChartOverride *chart.Chart + ValueOverride map[string]interface{} + Component types.ZarfComponent +} + +// InstallOrUpgradeChart performs a helm install of the given chart +func InstallOrUpgradeChart(options ChartOptions) ConnectStrings { + fromMessage := options.Chart.Url + if fromMessage == "" { + fromMessage = "Zarf-generated helm chart" + } + spinner := message.NewProgressSpinner("Processing helm chart %s:%s from %s", + options.Chart.Name, + options.Chart.Version, + fromMessage) + defer spinner.Stop() + + var output *release.Release + + options.ReleaseName = fmt.Sprintf("zarf-%s", options.Chart.Name) + actionConfig, err := createActionConfig(options.Chart.Namespace, spinner) + postRender := NewRenderer(options, actionConfig) + + // Setup K8s connection + if err != nil { + spinner.Fatalf(err, "Unable to initialize the K8s client") + } + + attempt := 0 + for { + attempt++ + + spinner.Updatef("Attempt %d of 3 to install chart", attempt) + histClient := action.NewHistory(actionConfig) + histClient.Max = 1 + + if attempt > 2 { + // On total failure try to rollback or uninstall + if histClient.Version > 1 { + spinner.Updatef("Performing chart rollback") + _ = rollbackChart(actionConfig, options.ReleaseName) + } else { + spinner.Updatef("Performing chart uninstall") + _, _ = uninstallChart(actionConfig, options.ReleaseName) + } + spinner.Errorf(nil, "Unable to complete helm chart install/upgrade") + break + } + + spinner.Updatef("Checking for existing helm deployment") + + _, histErr := histClient.Run(options.ReleaseName) + + switch histErr { + case driver.ErrReleaseNotFound: + // No prior release, try to install it + spinner.Updatef("Attempting chart installation") + output, err = installChart(actionConfig, options, postRender) + + case nil: + // Otherwise, there is a prior release so upgrade it + spinner.Updatef("Attempting chart upgrade") + output, err = upgradeChart(actionConfig, options, postRender) + + default: + // 😭 things aren't working + spinner.Fatalf(err, "Unable to verify the chart installation status") + } + + if err != nil { + spinner.Debugf(err.Error()) + // Simply wait for dust to settle and try again + time.Sleep(10 * time.Second) + } else { + spinner.Debugf(output.Info.Description) + spinner.Success() + break + } + + } + + // return any collected connect strings for zarf connect + return postRender.connectStrings +} + +// TemplateChart generates a helm template from a given chart +func TemplateChart(options ChartOptions) (string, error) { + message.Debugf("helm.TemplateChart(%v)", options) + spinner := message.NewProgressSpinner("Templating helm chart %s", options.Chart.Name) + defer spinner.Stop() + + actionConfig, err := createActionConfig(options.Chart.Namespace, spinner) + + // Setup K8s connection + if err != nil { + return "", fmt.Errorf("unable to initialize the K8s client: %w", err) + } + + // Bind the helm action + client := action.NewInstall(actionConfig) + + client.DryRun = true + client.Replace = true // Skip the name check + client.ClientOnly = true + client.IncludeCRDs = true + + client.ReleaseName = fmt.Sprintf("zarf-%s", options.Chart.Name) + + // Namespace must be specified + client.Namespace = options.Chart.Namespace + + loadedChart, chartValues, err := loadChartData(options) + if err != nil { + return "", fmt.Errorf("unable to load chart data: %w", err) + } + + // Perform the loadedChart installation + templatedChart, err := client.Run(loadedChart, chartValues) + if err != nil { + return "", fmt.Errorf("error generating helm chart template: %w", err) + } + + spinner.Success() + + return templatedChart.Manifest, nil +} + +func GenerateChart(basePath string, manifest types.ZarfManifest, component types.ZarfComponent) ConnectStrings { + message.Debugf("helm.GenerateChart(%s, %v, %s)", basePath, manifest, component.Name) + spinner := message.NewProgressSpinner("Starting helm chart generation %s", manifest.Name) + defer spinner.Stop() + + // Use timestamp to help make a valid semver + now := time.Now() + + // Generate a new chart + tmpChart := new(chart.Chart) + tmpChart.Metadata = new(chart.Metadata) + tmpChart.Metadata.Name = fmt.Sprintf("raw-%s", manifest.Name) + // This is fun, increment forward in a semver-way using epoch so helm doesn't cry + tmpChart.Metadata.Version = fmt.Sprintf("0.1.%d", now.Unix()) + tmpChart.Metadata.APIVersion = chart.APIVersionV1 + + // Add the manifest files so helm does its thing + for _, file := range manifest.Files { + spinner.Updatef("Processing %s", file) + manifest := fmt.Sprintf("%s/%s", basePath, file) + data, err := ioutil.ReadFile(manifest) + if err != nil { + spinner.Fatalf(err, "Unable to read the manifest file contents") + } + tmpChart.Templates = append(tmpChart.Templates, &chart.File{Name: manifest, Data: data}) + } + + if manifest.DefaultNamespace == "" { + // Helm gets sad when you don't provide a namespace even though we aren't using helm templating + manifest.DefaultNamespace = corev1.NamespaceDefault + } + + // Generate the struct to pass to InstallOrUpgradeChart() + options := ChartOptions{ + BasePath: basePath, + Chart: types.ZarfChart{ + Name: tmpChart.Metadata.Name, + Version: tmpChart.Metadata.Version, + Namespace: manifest.DefaultNamespace, + }, + ChartOverride: tmpChart, + // We don't have any values because we do not expose them in the zarf.yaml currently + ValueOverride: map[string]interface{}{}, + // Images needed for eventual post-render templating + Component: component, + } + + spinner.Success() + + return InstallOrUpgradeChart(options) +} + +func installChart(actionConfig *action.Configuration, options ChartOptions, postRender *renderer) (*release.Release, error) { + message.Debugf("helm.installChart(%v, %v, %v)", actionConfig, options, postRender) + // Bind the helm action + client := action.NewInstall(actionConfig) + + // Let each chart run for 5 minutes + client.Timeout = 15 * time.Minute + + client.Wait = true + + // We need to include CRDs or operator installations will fail spectacularly + client.SkipCRDs = false + + // Must be unique per-namespace and < 53 characters. @todo: restrict helm loadedChart name to this + client.ReleaseName = options.ReleaseName + + // Namespace must be specified + client.Namespace = options.Chart.Namespace + + // Post-processing our manifests for reasons.... + client.PostRenderer = postRender + + loadedChart, chartValues, err := loadChartData(options) + if err != nil { + return nil, fmt.Errorf("unable to load chart data: %w", err) + } + + // Perform the loadedChart installation + return client.Run(loadedChart, chartValues) +} + +func upgradeChart(actionConfig *action.Configuration, options ChartOptions, postRender *renderer) (*release.Release, error) { + message.Debugf("helm.upgradeChart(%v, %v, %v)", actionConfig, options, postRender) + client := action.NewUpgrade(actionConfig) + + // Let each chart run for 5 minutes + client.Timeout = 10 * time.Minute + + client.Wait = true + + client.SkipCRDs = true + + // Namespace must be specified + client.Namespace = options.Chart.Namespace + + // Post-processing our manifests for reasons.... + client.PostRenderer = postRender + + loadedChart, chartValues, err := loadChartData(options) + if err != nil { + return nil, fmt.Errorf("unable to load chart data: %w", err) + } + + // Perform the loadedChart upgrade + return client.Run(options.ReleaseName, loadedChart, chartValues) +} + +func rollbackChart(actionConfig *action.Configuration, name string) error { + message.Debugf("helm.rollbackChart(%v, %s)", actionConfig, name) + client := action.NewRollback(actionConfig) + client.CleanupOnFail = true + client.Force = true + client.Wait = true + client.Timeout = 1 * time.Minute + return client.Run(name) +} + +func uninstallChart(actionConfig *action.Configuration, name string) (*release.UninstallReleaseResponse, error) { + message.Debugf("helm.uninstallChart(%v, %s)", actionConfig, name) + client := action.NewUninstall(actionConfig) + client.KeepHistory = false + client.Timeout = 3 * time.Minute + client.Wait = true + return client.Run(name) +} + +func loadChartData(options ChartOptions) (*chart.Chart, map[string]interface{}, error) { + message.Debugf("helm.loadChartData(%v)", options) + var ( + loadedChart *chart.Chart + chartValues map[string]interface{} + err error + ) + + if options.ChartOverride == nil || options.ValueOverride == nil { + // If there is no override, get the chart and values info + loadedChart, err = loadChartFromTarball(options) + if err != nil { + return nil, nil, fmt.Errorf("unable to load chart tarball: %w", err) + } + + chartValues, err = parseChartValues(options) + if err != nil { + return loadedChart, nil, fmt.Errorf("unable to parse chart values: %w", err) + } + message.Debug(chartValues) + } else { + // Otherwise, use the overrides instead + loadedChart = options.ChartOverride + chartValues = options.ValueOverride + } + + return loadedChart, chartValues, nil +} diff --git a/cli/internal/helm/charts.go b/cli/internal/helm/charts.go deleted file mode 100644 index 42b9e37afc..0000000000 --- a/cli/internal/helm/charts.go +++ /dev/null @@ -1,98 +0,0 @@ -package helm - -import ( - "os" - - "github.com/defenseunicorns/zarf/cli/config" - "github.com/defenseunicorns/zarf/cli/internal/git" - "github.com/sirupsen/logrus" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli" - - "strings" - - "helm.sh/helm/v3/pkg/downloader" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/repo" -) - -func DownloadChartFromGit(chart config.ZarfChart, destination string) { - logContext := logrus.WithFields(logrus.Fields{ - "Chart": chart.Name, - "URL": chart.Url, - "Version": chart.Version, - }) - - logContext.Info("Processing git-based helm chart") - client := action.NewPackage() - - // Get the git repo - tempPath := git.DownloadRepoToTemp(chart.Url) - - // Switch to the correct tag - git.CheckoutTag(tempPath, chart.Version) - - // Tell helm where to save the archive and create the package - client.Destination = destination - name, err := client.Run(tempPath+"/chart", nil) - - if err != nil { - logContext.Debug(err) - logContext.Fatal("Helm is unable to save the archive and create the package:", name) - } - - _ = os.RemoveAll(tempPath) -} - -func DownloadPublishedChart(chart config.ZarfChart, destination string) { - logContext := logrus.WithFields(logrus.Fields{ - "Chart": chart.Name, - "URL": chart.Url, - "Version": chart.Version, - }) - - logContext.Info("Processing published helm chart") - - var out strings.Builder - - // Setup the helm pull config - pull := action.NewPull() - pull.Settings = cli.New() - - // Setup the chart downloader - downloader := downloader.ChartDownloader{ - Out: &out, - Verify: downloader.VerifyNever, - Getters: getter.All(pull.Settings), - } - - // @todo: process OCI-based charts - - // Perform simple chart download - chartURL, err := repo.FindChartInRepoURL(chart.Url, chart.Name, chart.Version, pull.CertFile, pull.KeyFile, pull.CaFile, getter.All(pull.Settings)) - if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to pull the helm chart") - } - - // Download the file (we don't control what name helm creates here) - saved, _, err := downloader.DownloadTo(chartURL, pull.Version, destination) - if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to download the helm chart") - } - - // Ensure the name is consistent for deployments - destinationTarball := StandardName(destination, chart) - err = os.Rename(saved, destinationTarball) - - if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to rename tarball") - } -} - -// StandardName generates a predictable full path for a helm chart for Zarf -func StandardName(destintation string, chart config.ZarfChart) string { - return destintation + "/" + chart.Name + "-" + chart.Version + ".tgz" -} diff --git a/cli/internal/helm/destroy.go b/cli/internal/helm/destroy.go new file mode 100644 index 0000000000..f83480e62d --- /dev/null +++ b/cli/internal/helm/destroy.go @@ -0,0 +1,60 @@ +package helm + +import ( + "github.com/defenseunicorns/zarf/cli/internal/message" + "helm.sh/helm/v3/pkg/action" + "regexp" +) + +func Destroy(purgeAllZarfInstallations bool) { + spinner := message.NewProgressSpinner("Removing Zarf-installed charts") + defer spinner.Stop() + + // Initially load the actionConfig without a namespace + actionConfig, err := createActionConfig("", spinner) + if err != nil { + // Don't fatal since this is a removal action + spinner.Errorf(err, "Unable to initialize the K8s client") + return + } + + // Match a name that begins with "zarf-" + // Explanation: https://regex101.com/r/3yzKZy/1 + zarfPrefix := regexp.MustCompile(`(?m)^zarf-`) + + // Get a list of all releases in all namespaces + list := action.NewList(actionConfig) + list.All = true + list.AllNamespaces = true + // Uninstall in reverse order + list.ByDate = true + list.SortReverse = true + releases, err := list.Run() + if err != nil { + // Don't fatal since this is a removal action + spinner.Errorf(err, "Unable to get the list of installed charts") + } + + // Iterate over all releases + for _, release := range releases { + if !purgeAllZarfInstallations && release.Namespace != "zarf" { + // Don't process releases outside the zarf namespace unless purgae all is true + continue + } + // Filter on zarf releases + if zarfPrefix.MatchString(release.Name) { + spinner.Updatef("Uninstalling helm chart %s/%s", release.Namespace, release.Name) + // Establish a new actionConfig for the namespace + actionConfig, _ = createActionConfig(release.Namespace, spinner) + // Perform the uninstall + response, err := uninstallChart(actionConfig, release.Name) + message.Debug(response) + if err != nil { + // Don't fatal since this is a removal action + spinner.Errorf(err, "Unable to uninstall the chart") + } + } + } + + spinner.Success() +} diff --git a/cli/internal/helm/post-render.go b/cli/internal/helm/post-render.go new file mode 100644 index 0000000000..834625ab95 --- /dev/null +++ b/cli/internal/helm/post-render.go @@ -0,0 +1,252 @@ +package helm + +import ( + "bytes" + "fmt" + "os" + "time" + + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/k8s" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/internal/utils" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/releaseutil" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/yaml" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +const managedByLabel = "app.kubernetes.io/managed-by" + +var secretName = "zarf-registry" + +type renderer struct { + actionConfig *action.Configuration + connectStrings ConnectStrings + options ChartOptions + namespaces map[string]*corev1.Namespace +} + +func NewRenderer(options ChartOptions, actionConfig *action.Configuration) *renderer { + message.Debugf("helm.NewRenderer(%v)", options) + return &renderer{ + actionConfig: actionConfig, + connectStrings: make(ConnectStrings), + options: options, + namespaces: map[string]*corev1.Namespace{ + // Add the passed-in namespace to the list + options.Chart.Namespace: nil, + }, + } +} + +func (r *renderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { + message.Debugf("helm.Run(renderedManifests *bytes.Buffer)") + // This is very low cost and consistent for how we replace elsewhere, also good for debugging + tempDir, _ := utils.MakeTempDir() + path := tempDir + "/chart.yaml" + + if r.options.Component.SecretName != "" { + // A custom secret name was given for this component + secretName = r.options.Component.SecretName + message.Debugf("using custom zarf secret name %s", secretName) + } + + // Write the context to a file for processing + if err := utils.WriteFile(path, renderedManifests.Bytes()); err != nil { + return nil, fmt.Errorf("unable to write the post-render file for the helm chart") + } + + // Run the template engine against the chart output + k8s.ProcessYamlFilesInPath(tempDir, r.options.Component.Images) + + // Read back the templated file contents + buff, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("error reading temporary post-rendered helm chart: %w", err) + } + + // Use helm to resplit the manifest byte (same call used by helm to pass this data to postRender) + _, resources, err := releaseutil.SortManifests(map[string]string{path: string(buff)}, + r.actionConfig.Capabilities.APIVersions, + releaseutil.InstallOrder, + ) + + if err != nil { + return nil, fmt.Errorf("error re-rendering helm output: %w", err) + } + + // Dump the contents for debugging + message.Debug(resources) + + finalManifestsOutput := bytes.NewBuffer(nil) + + if err != nil { + // On error only drop a warning + message.Errorf(err, "Problem parsing post-render manifest data") + } else { + // Otherwise, loop over the resources, + for _, resource := range resources { + + // parse to unstructured to have access to more data than just the name + rawData := &unstructured.Unstructured{} + if err := yaml.Unmarshal([]byte(resource.Content), rawData); err != nil { + return nil, fmt.Errorf("failed to unmarshal manifest: %v", err) + } + + switch rawData.GetKind() { + case "Namespace": + var namespace corev1.Namespace + // parse the namespace resource so it can be applied out-of-band by zarf instead of helm to avoid helm ns shennanigans + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(rawData.UnstructuredContent(), &namespace); err != nil { + message.Errorf(err, "could not parse namespace %s", rawData.GetName()) + } else { + message.Debugf("Matched helm namespace %s for zarf annotation", &namespace.Name) + if namespace.Labels == nil { + // Ensure label map exists to avoid nil panic + namespace.Labels = make(map[string]string) + } + // Now track this namespace by zarf + namespace.Labels[managedByLabel] = "zarf" + namespace.Labels["zarf-helm-release"] = r.options.ReleaseName + + // Add it to the stack + r.namespaces[namespace.Name] = &namespace + } + // skip so we can strip namespaces from helms brain + continue + + case "ServiceAccount": + var svcAccount corev1.ServiceAccount + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(rawData.UnstructuredContent(), &svcAccount); err != nil { + message.Errorf(err, "could not parse service account %s", rawData.GetName()) + } else { + message.Debugf("Matched helm svc account %s for zarf annotation", &svcAccount.Name) + + // Add the zarf image pull secret to the sa + svcAccount.ImagePullSecrets = append(svcAccount.ImagePullSecrets, corev1.LocalObjectReference{ + Name: secretName, + }) + + if byteData, err := yaml.Marshal(svcAccount); err != nil { + message.Error(err, "unable to marshal svc account") + } else { + // Update the contents of the svc account + resource.Content = string(byteData) + } + } + + case "Service": + // Check service resources for the zarf-connect label + labels := rawData.GetLabels() + annotations := rawData.GetAnnotations() + + if key, keyExists := labels[config.ZarfConnectLabelName]; keyExists { + // If there is a zarf-connect label + message.Debugf("Match helm service %s for zarf connection %s", rawData.GetName(), key) + + // Add the connectstring for processing later in the deployment + r.connectStrings[key] = ConnectString{ + Description: annotations[config.ZarfConnectAnnotationDescription], + Url: annotations[config.ZarfConnectAnnotationUrl], + } + } + } + + namespace := rawData.GetNamespace() + if _, exists := r.namespaces[namespace]; !exists && namespace != "" { + // if this is the first time seeing this ns, we need to track that to create it as well + r.namespaces[namespace] = nil + } + + // Finally place this back onto the output buffer + fmt.Fprintf(finalManifestsOutput, "---\n# Source: %s\n%s\n", resource.Name, resource.Content) + } + } + + existingNamespaces, _ := k8s.GetNamespaces() + + for name, namespace := range r.namespaces { + + // Check to see if this namespace already exists + var existingNamespace bool + for _, serverNamespace := range existingNamespaces.Items { + if serverNamespace.Name == name { + existingNamespace = true + } + } + + if !existingNamespace { + // This is a new namespace, add it + if _, err := k8s.CreateNamespace(name, namespace); err != nil { + return nil, fmt.Errorf("unable to create the missing namespace %s", name) + } + } + + // Try to get an existing secret + if secret, _ := k8s.GetSecret(name, secretName); secret.Name != secretName { + // create the missing zarf secret + secret = k8s.GenerateRegistryPullCreds(name, secretName) + if err := k8s.CreateSecret(secret); err != nil { + message.Errorf(err, "Problem creating registry secret for the %s namespace", name) + } + } + + // Attempt to update the default service account + attemptsLeft := 5 + for attemptsLeft > 0 { + err = updateDefaultSvcAccount(name) + if err == nil { + break + } else { + attemptsLeft-- + time.Sleep(1 * time.Second) + } + } + if err != nil { + message.Errorf(err, "Unable to update the default service account for the %s namespace", name) + } + + } + + // Cleanup the temp file + _ = os.RemoveAll(tempDir) + + // Send the bytes back to helm + return finalManifestsOutput, nil +} + +func updateDefaultSvcAccount(namespace string) error { + + // Get the default service account from the provided namespace + defaultSvcAccount, err := k8s.GetServiceAccount(namespace, corev1.NamespaceDefault) + if err != nil { + return fmt.Errorf("unable to get service accounts for namespace %s", namespace) + } + + // Look to see if the service account needs to be patched + if defaultSvcAccount.Labels[managedByLabel] != "zarf" { + // This service account needs the pull secret added + defaultSvcAccount.ImagePullSecrets = append(defaultSvcAccount.ImagePullSecrets, corev1.LocalObjectReference{ + Name: secretName, + }) + + if defaultSvcAccount.Labels == nil { + // Ensure label map exists to avoid nil panic + defaultSvcAccount.Labels = make(map[string]string) + } + + // Track this by zarf + defaultSvcAccount.Labels[managedByLabel] = "zarf" + + // Finally update the chnage on the server + if _, err := k8s.SaveServiceAccount(defaultSvcAccount); err != nil { + return fmt.Errorf("unable to update the default service account for the %s namespace: %w", defaultSvcAccount.Namespace, err) + } + } + + return nil +} diff --git a/cli/internal/helm/repo.go b/cli/internal/helm/repo.go new file mode 100644 index 0000000000..a90bafd0cd --- /dev/null +++ b/cli/internal/helm/repo.go @@ -0,0 +1,82 @@ +package helm + +import ( + "github.com/defenseunicorns/zarf/cli/types" + "os" + + "github.com/defenseunicorns/zarf/cli/internal/git" + "github.com/defenseunicorns/zarf/cli/internal/message" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/cli" + + "helm.sh/helm/v3/pkg/downloader" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/repo" +) + +// DownloadChartFromGit is a special implementation of chart downloads that support the https://p1.dso.mil/#/products/big-bang/ model +func DownloadChartFromGit(chart types.ZarfChart, destination string) string { + spinner := message.NewProgressSpinner("Processing helm chart %s", chart.Name) + defer spinner.Stop() + + client := action.NewPackage() + + // Get the git repo + tempPath := git.DownloadRepoToTemp(chart.Url, spinner) + + // Switch to the correct tag + git.CheckoutTag(tempPath, chart.Version) + + // Tell helm where to save the archive and create the package + client.Destination = destination + name, err := client.Run(tempPath+"/"+chart.GitPath, nil) + + if err != nil { + spinner.Fatalf(err, "Helm is unable to save the archive and create the package %s", name) + } + + _ = os.RemoveAll(tempPath) + spinner.Success() + + return name +} + +// DownloadPublishedChart loads a specific chart version from a remote repo +func DownloadPublishedChart(chart types.ZarfChart, destination string) { + spinner := message.NewProgressSpinner("Processing helm chart %s:%s from repo %s", chart.Name, chart.Version, chart.Url) + defer spinner.Stop() + + // Set up the helm pull config + pull := action.NewPull() + pull.Settings = cli.New() + + // Set up the chart chartDownloader + chartDownloader := downloader.ChartDownloader{ + Out: spinner, + Verify: downloader.VerifyNever, + Getters: getter.All(pull.Settings), + } + + // @todo: process OCI-based charts + + // Perform simple chart download + chartURL, err := repo.FindChartInRepoURL(chart.Url, chart.Name, chart.Version, pull.CertFile, pull.KeyFile, pull.CaFile, getter.All(pull.Settings)) + if err != nil { + spinner.Fatalf(err, "Unable to pull the helm chart") + } + + // Download the file (we don't control what name helm creates here) + saved, _, err := chartDownloader.DownloadTo(chartURL, pull.Version, destination) + if err != nil { + spinner.Fatalf(err, "Unable to download the helm chart") + } + + // Ensure the name is consistent for deployments + destinationTarball := StandardName(destination, chart) + ".tgz" + err = os.Rename(saved, destinationTarball) + if err != nil { + spinner.Fatalf(err, "Unable to save the chart tarball") + } + + spinner.Success() +} diff --git a/cli/internal/helm/utils.go b/cli/internal/helm/utils.go new file mode 100644 index 0000000000..4e585b0100 --- /dev/null +++ b/cli/internal/helm/utils.go @@ -0,0 +1,79 @@ +package helm + +import ( + "fmt" + "github.com/defenseunicorns/zarf/cli/types" + "os" + "strconv" + + "github.com/defenseunicorns/zarf/cli/internal/message" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/cli/values" + "helm.sh/helm/v3/pkg/getter" + + "helm.sh/helm/v3/pkg/chart/loader" +) + +// StandardName generates a predictable full path for a helm chart for Zarf +func StandardName(destination string, chart types.ZarfChart) string { + return destination + "/" + chart.Name + "-" + chart.Version +} + +// loadChartFromTarball returns a helm chart from a tarball +func loadChartFromTarball(options ChartOptions) (*chart.Chart, error) { + // Get the path the temporary helm chart tarball + sourceFile := StandardName(options.BasePath+"/charts", options.Chart) + ".tgz" + if options.ChartLoadOverride != "" { + sourceFile = options.ChartLoadOverride + } + + // Load the loadedChart tarball + loadedChart, err := loader.Load(sourceFile) + if err != nil { + return nil, fmt.Errorf("unable to load helm chart archive: %w", err) + } + + if err = loadedChart.Validate(); err != nil { + return nil, fmt.Errorf("unable to validate loaded helm chart: %w", err) + } + + return loadedChart, nil +} + +// parseChartValues reads the context of the chart values into an interface if it exists +func parseChartValues(options ChartOptions) (map[string]interface{}, error) { + valueOpts := &values.Options{} + + for idx, file := range options.Chart.ValuesFiles { + path := StandardName(options.BasePath+"/values", options.Chart) + "-" + strconv.Itoa(idx) + // If we are overriding the chart path, assuming this is for zarf prepare + if options.ChartLoadOverride != "" { + path = file + } + valueOpts.ValueFiles = append(valueOpts.ValueFiles, path) + } + + httpProvider := getter.Provider{ + Schemes: []string{"http", "https"}, + New: getter.NewHTTPGetter, + } + + providers := getter.Providers{httpProvider} + return valueOpts.MergeValues(providers) +} + +func createActionConfig(namespace string, spinner *message.Spinner) (*action.Configuration, error) { + // OMG THIS IS SOOOO GROSS PPL... https://github.com/helm/helm/issues/8780 + _ = os.Setenv("HELM_NAMESPACE", namespace) + + // Initialize helm SDK + actionConfig := new(action.Configuration) + settings := cli.New() + + // Setup K8s connection + err := actionConfig.Init(settings.RESTClientGetter(), namespace, "", spinner.Updatef) + + return actionConfig, err +} diff --git a/cli/internal/images/common.go b/cli/internal/images/common.go new file mode 100644 index 0000000000..9a9e665c6b --- /dev/null +++ b/cli/internal/images/common.go @@ -0,0 +1,13 @@ +package images + +import ( + "fmt" + "os" +) + +var cachePath = ".zarf-image-cache" + +func init() { + homePath, _ := os.UserHomeDir() + cachePath = fmt.Sprintf("%s/%s", homePath, cachePath) +} diff --git a/cli/internal/images/copy.go b/cli/internal/images/copy.go new file mode 100644 index 0000000000..88b992f143 --- /dev/null +++ b/cli/internal/images/copy.go @@ -0,0 +1,13 @@ +package images + +import ( + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/google/go-containerregistry/pkg/crane" +) + +func Copy(src string, dest string) { + if err := crane.Copy(src, dest, config.ActiveCranePlatform); err != nil { + message.Fatal(err, "Unable to copy the image") + } +} diff --git a/cli/internal/images/pull.go b/cli/internal/images/pull.go index 5e9b29d5d9..716b2aa763 100644 --- a/cli/internal/images/pull.go +++ b/cli/internal/images/pull.go @@ -1,33 +1,111 @@ package images import ( + "errors" + "fmt" + "io" + + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/internal/utils" "github.com/google/go-containerregistry/pkg/crane" + "github.com/google/go-containerregistry/pkg/logs" + "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/cache" - "github.com/sirupsen/logrus" + "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/pterm/pterm" ) -const cachePath = ".image-cache" - func PullAll(buildImageList []string, imageTarballPath string) { - logrus.Info("Loading images") - cranePlatformOptions := crane.WithPlatform(&v1.Platform{OS: "linux", Architecture: "amd64"}) + var ( + longer string + imageCount = len(buildImageList) + ) + + // Give some additional user feedback on larger image sets + if imageCount > 15 { + longer = "This step may take a couple of minutes to complete." + } else if imageCount > 5 { + longer = "This step may take several seconds to complete." + } + + spinner := message.NewProgressSpinner("Loading metadata for %d images. %s", imageCount, longer) + defer spinner.Stop() + imageMap := map[string]v1.Image{} - for _, src := range buildImageList { - logContext := logrus.WithField("image", src) - logContext.Info("Fetching image metadata") - img, err := crane.Pull(src, cranePlatformOptions) + if message.GetLogLevel() >= message.DebugLevel { + logs.Warn.SetOutput(spinner) + logs.Progress.SetOutput(spinner) + } + + for idx, src := range buildImageList { + spinner.Updatef("Fetching image metadata (%d of %d): %s", idx+1, imageCount, src) + img, err := crane.Pull(src, config.ActiveCranePlatform) if err != nil { - logContext.Warn("Unable to pull the image") + spinner.Fatalf(err, "Unable to pull the image %s", src) } img = cache.Image(img, cache.NewFilesystemCache(cachePath)) imageMap[src] = img } - logrus.Info("Creating image tarball (this will take a while)") - if err := crane.MultiSave(imageMap, imageTarballPath); err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to save the tarball") + spinner.Updatef("Creating image tarball (this will take a while)") + + tagToImage := map[name.Tag]v1.Image{} + + for src, img := range imageMap { + ref, err := name.ParseReference(src) + if err != nil { + spinner.Fatalf(err, "parsing ref %q", src) + } + + tag, ok := ref.(name.Tag) + if !ok { + d, ok := ref.(name.Digest) + if !ok { + spinner.Fatalf(nil, "image reference %s wasn't a tag or digest", src) + } + tag = d.Repository.Tag("digest-only") + } + tagToImage[tag] = img + } + spinner.Success() + + progress := make(chan v1.Update, 200) + + go func() { + _ = tarball.MultiWriteToFile(imageTarballPath, tagToImage, tarball.WithProgress(progress)) + }() + + var progressBar *pterm.ProgressbarPrinter + var title string + + for update := range progress { + switch { + case update.Error != nil && errors.Is(update.Error, io.EOF): + _, _ = progressBar.Stop() + title = fmt.Sprintf("Pulling %v images (%s)", len(imageMap), utils.ByteFormat(float64(update.Total), 2)) + pterm.Success.Println(title) + return + case update.Error != nil: + message.Fatal(update.Error, "error writing image tarball") + default: + title = fmt.Sprintf("Pulling %v images (%s of %s)", len(imageMap), + utils.ByteFormat(float64(update.Complete), 2), + utils.ByteFormat(float64(update.Total), 2), + ) + if progressBar == nil { + progressBar, _ = pterm.DefaultProgressbar. + WithTotal(int(update.Total)). + WithShowCount(false). + WithTitle(title). + WithRemoveWhenDone(true). + Start() + } + progressBar.UpdateTitle(title) + chunk := int(update.Complete) - progressBar.Current + progressBar.Add(chunk) + } } } diff --git a/cli/internal/images/push.go b/cli/internal/images/push.go index b744fa71bb..bec8a27bf2 100644 --- a/cli/internal/images/push.go +++ b/cli/internal/images/push.go @@ -1,43 +1,39 @@ package images import ( - "regexp" - + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/k8s" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/internal/utils" "github.com/google/go-containerregistry/pkg/crane" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/sirupsen/logrus" ) -func PushAll(imageTarballPath string, buildImageList []string, targetHost string) { - cranePlatformOptions := crane.WithPlatform(&v1.Platform{OS: "linux", Architecture: "amd64"}) +func PushToZarfRegistry(imageTarballPath string, buildImageList []string, target string) { + // Establish a registry tunnel to send the images if pushing to the zarf registry + if target == config.ZarfRegistry { + tunnel := k8s.NewZarfTunnel() + tunnel.Connect(k8s.ZarfRegistry, false) + defer tunnel.Close() + } + + spinner := message.NewProgressSpinner("Storing images in the zarf registry") + defer spinner.Stop() for _, src := range buildImageList { - logContext := logrus.WithFields(logrus.Fields{ - "source": src, - "target": targetHost, - }) - logContext.Info("Updating image") - img, err := crane.LoadTag(imageTarballPath, src, cranePlatformOptions) + spinner.Updatef("Updating image %s", src) + img, err := crane.LoadTag(imageTarballPath, src, config.ActiveCranePlatform) if err != nil { - logContext.Debug(err) - logContext.Warn("Unable to load the image from the update package") + spinner.Errorf(err, "Unable to load the image from the update package") return } - offlineName := SwapHost(src, targetHost) + offlineName := utils.SwapHost(src, target) - err = crane.Push(img, offlineName, cranePlatformOptions) + err = crane.Push(img, offlineName, config.ActiveCranePlatform) if err != nil { - logContext.Debug(err) - logContext.Warn("Unable to push the image to the registry") + spinner.Errorf(err, "Unable to push the image to the registry") } } -} -// SwapHost Perform base url replacment without the docker libs -func SwapHost(src string, targetHost string) string { - // For further explanation see https://regex101.com/library/PiL191 and https://regex101.com/r/PiL191/1 - var parser = regexp.MustCompile(`(?im)^([a-z0-9\-.]+\.[a-z0-9\-]+:?[0-9]*)?/?(.+)$`) - var substitution = targetHost + "/$2" - return parser.ReplaceAllString(src, substitution) + spinner.Success() } diff --git a/cli/internal/k8s/common.go b/cli/internal/k8s/common.go index 37148f0030..26503f3cfd 100644 --- a/cli/internal/k8s/common.go +++ b/cli/internal/k8s/common.go @@ -1,27 +1,58 @@ package k8s import ( + "bytes" "fmt" + "github.com/go-logr/logr" + "io" "io/ioutil" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog/v2" + "os" + "regexp" - "github.com/sirupsen/logrus" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/internal/template" + "github.com/defenseunicorns/zarf/cli/internal/utils" + "github.com/go-logr/logr/funcr" + kubeyaml "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/yaml" ) -func connect() *kubernetes.Clientset { - kubeconfig := "/root/.kube/config" +// ImageSwap Pre-compute all the replacements for the embedded registry +type ImageSwap struct { + find string + replace string +} + +func init() { + klog.SetLogger(generateLogShim()) +} + +func getRestConfig() *rest.Config { + homePath, err := os.UserHomeDir() + if err != nil { + message.Fatal(nil, "Unable to load the current user's home directory") + } // use the current context in kubeconfig - config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + config, err := clientcmd.BuildConfigFromFlags("", homePath+"/.kube/config") if err != nil { - logrus.Fatal("Unable to connect to the K8s cluster", err.Error()) + message.Fatalf(err, "Unable to connect to the K8s cluster") } + return config +} +func getClientset() *kubernetes.Clientset { + config := getRestConfig() // create the clientset clientset, err := kubernetes.NewForConfig(config) if err != nil { - logrus.Fatal("Unable to connect to the K8s cluster", err.Error()) + message.Fatal(err, "Unable to connect to the K8s cluster") } return clientset @@ -31,8 +62,105 @@ func connect() *kubernetes.Clientset { func readFile(file string) ([]byte, error) { b, err := ioutil.ReadFile(file) if err != nil { - logrus.Debug(err) + message.Debug(err) return []byte{}, fmt.Errorf("cannot read file %v, %v", file, err) } return b, nil } + +func GetContext() (string, error) { + kubeconfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + clientcmd.NewDefaultClientConfigLoadingRules(), + &clientcmd.ConfigOverrides{}, + ) + kubeconfig.ConfigAccess().GetLoadingPrecedence() + kubeConf, err := kubeconfig.ConfigAccess().GetStartingConfig() + if err != nil { + return "", fmt.Errorf("unable to load the default kube config") + } + + return kubeConf.CurrentContext, nil +} + +// ProcessYamlFilesInPath iterates over all yaml files in a given path and performs Zarf templating + image swapping +func ProcessYamlFilesInPath(path string, componentImages []string) []string { + message.Debugf("k8s.ProcessYamlFilesInPath(%s, %v)", path, componentImages) + + // Only pull in yml and yaml files + pattern := regexp.MustCompile(`(?mi)\.ya?ml$`) + manifests := utils.RecursiveFileList(path, pattern) + valueTemplate := template.Generate() + + // Match images in the given list and replace if found in the given files + var imageSwap []ImageSwap + for _, image := range componentImages { + imageSwap = append(imageSwap, ImageSwap{ + find: image, + replace: utils.SwapHost(image, valueTemplate.GetRegistry()), + }) + } + + for _, manifest := range manifests { + message.Debugf("Processing k8s manifest files %s", manifest) + // Iterate over each image swap to see if it exists in the manifest + for _, swap := range imageSwap { + utils.ReplaceText(manifest, swap.find, swap.replace) + } + valueTemplate.Apply(manifest) + } + + return manifests +} + +func generateLogShim() logr.Logger { + message.Debug("k8s.generateLogShim()") + return funcr.New(func(prefix, args string) { + message.Debug(args) + }, funcr.Options{}) +} + +// SplitYAML splits a YAML file into unstructured objects. Returns list of all unstructured objects +// found in the yaml. If an error occurs, returns objects that have been parsed so far too. +// Source: https://github.com/argoproj/gitops-engine/blob/v0.5.2/pkg/utils/kube/kube.go#L286 +func SplitYAML(yamlData []byte) ([]*unstructured.Unstructured, error) { + var objs []*unstructured.Unstructured + ymls, err := splitYAMLToString(yamlData) + if err != nil { + return nil, err + } + for _, yml := range ymls { + u := &unstructured.Unstructured{} + if err := yaml.Unmarshal([]byte(yml), u); err != nil { + return objs, fmt.Errorf("failed to unmarshal manifest: %v", err) + } + objs = append(objs, u) + } + return objs, nil +} + +// splitYAMLToString splits a YAML file into strings. Returns list of yamls +// found in the yaml. If an error occurs, returns objects that have been parsed so far too. +// Source: https://github.com/argoproj/gitops-engine/blob/v0.5.2/pkg/utils/kube/kube.go#L304 +func splitYAMLToString(yamlData []byte) ([]string, error) { + // Similar way to what kubectl does + // https://github.com/kubernetes/cli-runtime/blob/master/pkg/resource/visitor.go#L573-L600 + // Ideally k8s.io/cli-runtime/pkg/resource.Builder should be used instead of this method. + // E.g. Builder does list unpacking and flattening and this code does not. + d := kubeyaml.NewYAMLOrJSONDecoder(bytes.NewReader(yamlData), 4096) + var objs []string + for { + ext := runtime.RawExtension{} + if err := d.Decode(&ext); err != nil { + if err == io.EOF { + break + } + return objs, fmt.Errorf("failed to unmarshal manifest: %v", err) + } + ext.Raw = bytes.TrimSpace(ext.Raw) + if len(ext.Raw) == 0 || bytes.Equal(ext.Raw, []byte("null")) { + continue + } + objs = append(objs, string(ext.Raw)) + } + return objs, nil +} diff --git a/cli/internal/k8s/distro.go b/cli/internal/k8s/distro.go new file mode 100644 index 0000000000..d85ffd5764 --- /dev/null +++ b/cli/internal/k8s/distro.go @@ -0,0 +1,74 @@ +package k8s + +import ( + "fmt" + "regexp" +) + +const ( + DistroIsUnknown = "unknown" + DistroIsK3s = "k3s" + DistroIsK3d = "k3d" + DistroIsKind = "kind" + DistroIsMicroK8s = "microk8s" + DistroIsEKSAnywhere = "eksanywhere" + DistroIsDockerDesktop = "dockerdesktop" + + // todo: more distros +) + +func DetectDistro() (string, error) { + kindNodeRegex := regexp.MustCompile(`^kind://`) + k3dNodeRegex := regexp.MustCompile(`^k3s://k3d-`) + + nodes, err := GetNodes() + if err != nil { + return DistroIsUnknown, fmt.Errorf("error getting cluster nodes") + } + + // Iterate over the nodes looking for label matches + for _, node := range nodes.Items { + // Regex explanation: https://regex101.com/r/TIUQVe/1 + // https://github.com/rancher/k3d/blob/v5.2.2/cmd/node/nodeCreate.go#L187 + if k3dNodeRegex.MatchString(node.Spec.ProviderID) { + return DistroIsK3d, nil + } + + // Regex explanation: https://regex101.com/r/le7PRB/1 + // https://github.com/kubernetes-sigs/kind/pull/1805 + if kindNodeRegex.MatchString(node.Spec.ProviderID) { + return DistroIsKind, nil + } + + labels := node.GetLabels() + for _, label := range labels { + // kubectl get nodes --selector node.kubernetes.io/instance-type=k3s for K3s + if label == "node.kubernetes.io/instance-type=k3s" { + return DistroIsK3s, nil + } + // kubectl get nodes --selector microk8s.io/cluster=true for MicroK8s + if label == "microk8s.io/cluster=true" { + return DistroIsMicroK8s, nil + } + } + + if node.GetName() == "docker-desktop" { + return DistroIsDockerDesktop, nil + } + + } + + namespaces, err := GetNamespaces() + if err != nil { + return DistroIsUnknown, fmt.Errorf("error getting namesapce list") + } + + // kubectl get ns eksa-system for EKS Anywhere + for _, namespace := range namespaces.Items { + if namespace.Name == "eksa-system" { + return DistroIsEKSAnywhere, nil + } + } + + return DistroIsUnknown, nil +} diff --git a/cli/internal/k8s/namespace.go b/cli/internal/k8s/namespace.go new file mode 100644 index 0000000000..ff63a656ca --- /dev/null +++ b/cli/internal/k8s/namespace.go @@ -0,0 +1,83 @@ +package k8s + +import ( + "context" + "os" + "time" + + "github.com/defenseunicorns/zarf/cli/internal/message" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func GetNamespaces() (*corev1.NamespaceList, error) { + clientset := getClientset() + + metaOptions := metav1.ListOptions{} + return clientset.CoreV1().Namespaces().List(context.TODO(), metaOptions) +} + +func CreateNamespace(name string, namespace *corev1.Namespace) (*corev1.Namespace, error) { + message.Debugf("k8s.CreateNamespace(%s)", name) + + clientset := getClientset() + + if namespace == nil { + // if only a name was provided create the namespace object + namespace = &corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{ + // track the creation of this ns by zarf + "app.kubernetes.io/managed-by": "zarf", + }, + }, + } + } + + metaOptions := metav1.GetOptions{} + createOptions := metav1.CreateOptions{} + + match, err := clientset.CoreV1().Namespaces().Get(context.TODO(), name, metaOptions) + + message.Debug(match) + + if err != nil || match.Name != name { + return clientset.CoreV1().Namespaces().Create(context.TODO(), namespace, createOptions) + } + + return match, err +} + +func DeleteZarfNamespace() { + spinner := message.NewProgressSpinner("Deleting the zarf namespace from this cluster") + defer spinner.Stop() + + clientset := getClientset() + // Get the zarf ns and ignore errors + namespace, _ := clientset.CoreV1().Namespaces().Get(context.TODO(), ZarfNamespace, metav1.GetOptions{}) + // Remove the k8s finalizer to speed up destroy + _, _ = clientset.CoreV1().Namespaces().Finalize(context.TODO(), namespace, metav1.UpdateOptions{}) + + // Attempt to delete the namespace + err := clientset.CoreV1().Namespaces().Delete(context.TODO(), ZarfNamespace, metav1.DeleteOptions{}) + if err != nil && !errors.IsNotFound(err) { + spinner.Fatalf(err, "the Zarf namespace could not be deleted") + } + + spinner.Updatef("Zarf namespace deletion scheduled, waiting for all resources to be removed") + for { + // Keep checking for the + _, err := clientset.CoreV1().Namespaces().Get(context.TODO(), ZarfNamespace, metav1.GetOptions{}) + if errors.IsNotFound(err) { + spinner.Successf("Zarf removed from this cluster") + os.Exit(0) + } + time.Sleep(1 * time.Second) + } +} diff --git a/cli/internal/k8s/nodes.go b/cli/internal/k8s/nodes.go new file mode 100644 index 0000000000..ba95256626 --- /dev/null +++ b/cli/internal/k8s/nodes.go @@ -0,0 +1,15 @@ +package k8s + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func GetNodes() (*corev1.NodeList, error) { + clientset := getClientset() + + metaOptions := metav1.ListOptions{} + return clientset.CoreV1().Nodes().List(context.TODO(), metaOptions) +} diff --git a/cli/internal/k8s/pods.go b/cli/internal/k8s/pods.go index 70bcca1938..746c7819c3 100644 --- a/cli/internal/k8s/pods.go +++ b/cli/internal/k8s/pods.go @@ -2,43 +2,47 @@ package k8s import ( "context" + "github.com/defenseunicorns/zarf/cli/types" + "sort" "time" - "github.com/defenseunicorns/zarf/cli/config" - "github.com/sirupsen/logrus" - v1 "k8s.io/api/core/v1" + "github.com/defenseunicorns/zarf/cli/internal/message" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const waitLimit = 30 -func WaitForPodsAndContainers(target config.ZarfContainerTarget) []string { +// WaitForPodsAndContainers holds execution up to 30 seconds waiting for health pods and containers (if specified) +func WaitForPodsAndContainers(target types.ZarfContainerTarget, waitForAllPods bool) []string { - clientSet := connect() - logContext := logrus.WithFields(logrus.Fields{ - "Namespace": target.Namespace, - "Selector": target.Selector, - "Container": target.Container, - }) + clientSet := getClientset() + message.Debugf("Waiting for ready pod %s/%s", target.Namespace, target.Selector) for count := 0; count < waitLimit; count++ { - logContext.Info("Looking up K8s pod") pods, err := clientSet.CoreV1().Pods(target.Namespace).List(context.TODO(), metav1.ListOptions{ LabelSelector: target.Selector, }) if err != nil { - logContext.Warn("Unable to find matching pods", err.Error()) + message.Error(err, "Unable to find matching pods") break } var readyPods []string + // Reverse sort by creation time + sort.Slice(pods.Items, func(i, j int) bool { + return pods.Items[i].CreationTimestamp.After(pods.Items[j].CreationTimestamp.Time) + }) + if len(pods.Items) > 0 { for _, pod := range pods.Items { + message.Debugf("Testing pod %s", pod.Name) // Handle container targetting if target.Container != "" { + message.Debugf("Testing for container") var matchesInitContainer bool // Check the status of initContainers for a running match @@ -66,22 +70,29 @@ func WaitForPodsAndContainers(target config.ZarfContainerTarget) []string { } } else { + status := pod.Status.Phase + message.Debugf("Testing for pod only, phase: %s", status) // Regular status checking without a container - if pod.Status.Phase == v1.PodRunning { + if status == corev1.PodRunning { readyPods = append(readyPods, pod.Name) } } } - if len(pods.Items) == len(readyPods) { + message.Debug("Ready pods", readyPods) + somePodsReady := len(readyPods) > 0 + allPodsReady := len(pods.Items) == len(readyPods) + + if allPodsReady || somePodsReady && !waitForAllPods { return readyPods } + } time.Sleep(3 * time.Second) } - logContext.Warn("Pod lookup timeout exceeded") + message.Warn("Pod lookup timeout exceeded") return []string{} } diff --git a/cli/internal/k8s/sa.go b/cli/internal/k8s/sa.go new file mode 100644 index 0000000000..9325b06867 --- /dev/null +++ b/cli/internal/k8s/sa.go @@ -0,0 +1,33 @@ +package k8s + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func GetAllServiceAccounts() (*corev1.ServiceAccountList, error) { + return GetServiceAccounts(corev1.NamespaceAll) +} + +func GetServiceAccounts(namespace string) (*corev1.ServiceAccountList, error) { + clientset := getClientset() + + metaOptions := metav1.ListOptions{} + return clientset.CoreV1().ServiceAccounts(namespace).List(context.TODO(), metaOptions) +} + +func GetServiceAccount(namespace string, name string) (*corev1.ServiceAccount, error) { + clientset := getClientset() + + metaOptions := metav1.GetOptions{} + return clientset.CoreV1().ServiceAccounts(namespace).Get(context.TODO(), name, metaOptions) +} + +func SaveServiceAccount(svcAccount *corev1.ServiceAccount) (*corev1.ServiceAccount, error) { + clientset := getClientset() + + metaOptions := metav1.UpdateOptions{} + return clientset.CoreV1().ServiceAccounts(svcAccount.Namespace).Update(context.TODO(), svcAccount, metaOptions) +} diff --git a/cli/internal/k8s/secrets.go b/cli/internal/k8s/secrets.go index 79c8fc6455..def0f23617 100644 --- a/cli/internal/k8s/secrets.go +++ b/cli/internal/k8s/secrets.go @@ -3,46 +3,95 @@ package k8s import ( "context" "crypto/tls" + "encoding/base64" + "encoding/json" + "fmt" "github.com/defenseunicorns/zarf/cli/config" - "github.com/sirupsen/logrus" + "github.com/defenseunicorns/zarf/cli/internal/message" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func ReplaceTLSSecret(namespace string, name string) { +type DockerConfig struct { + Auths DockerConfigEntry `json:"auths"` +} - state := config.GetState() - clientSet := connect() - logContext := logrus.WithFields(logrus.Fields{ - "Namespace": namespace, - "Name": name, - "Cert": state.TLS.CertPublicPath, - }) - namespaceSecrets := clientSet.CoreV1().Secrets(namespace) +type DockerConfigEntry map[string]DockerConfigEntryWithAuth - logContext.Info("Loading secret") +type DockerConfigEntryWithAuth struct { + Auth string `json:"auth"` +} - err := namespaceSecrets.Delete(context.TODO(), name, metav1.DeleteOptions{}) - if err != nil && !errors.IsNotFound(err) { - logContext.Debug(err) - logContext.Warn("Error deleting the secret") +func GetSecret(namespace string, name string) (*corev1.Secret, error) { + message.Debugf("k8s.getSecret(%s, %s)", namespace, name) + clientSet := getClientset() + return clientSet.CoreV1().Secrets(namespace).Get(context.TODO(), name, metav1.GetOptions{}) +} + +func GenerateRegistryPullCreds(namespace string, name string) *corev1.Secret { + message.Debugf("k8s.GenerateRegistryPullCreds(%s, %s)", namespace, name) + + secretDockerConfig := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "zarf", + }, + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{}, + } + + // Auth field must be username:password and base64 encoded + credential := config.GetSecret(config.StateRegistryPull) + if credential == "" { + message.Fatalf(nil, "Generate pull cred failed") } + fieldValue := config.ZarfRegistryPullUser + ":" + credential + authEncodedValue := base64.StdEncoding.EncodeToString([]byte(fieldValue)) - tlsCert, err := readFile(state.TLS.CertPublicPath) + registry := config.GetRegistry() + // Create the expected structure for the dockerconfigjson + dockerConfigJSON := DockerConfig{ + Auths: DockerConfigEntry{ + registry: DockerConfigEntryWithAuth{ + Auth: authEncodedValue, + }, + }, + } + + // Convert to JSON + dockerConfigData, err := json.Marshal(dockerConfigJSON) + if err != nil { + message.Fatalf(err, "Unable to create the embedded registry secret") + } + + // Add to the secret data + secretDockerConfig.Data[".dockerconfigjson"] = dockerConfigData + + return secretDockerConfig +} + +func GenerateTLSSecret(namespace string, name string, certPath string, keyPath string) *corev1.Secret { + message.Debugf("k8s.GenerateTLSSecret(%s, %s, %s, %s", namespace, name, certPath, keyPath) + + tlsCert, err := readFile(certPath) if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to read the TLS public certificate") + message.Fatal(err, "Unable to read the TLS public certificate") } - tlsKey, err := readFile(state.TLS.CertPrivatePath) + tlsKey, err := readFile(keyPath) if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to read the TLS private key") + message.Fatal(err, "Unable to read the TLS private key") } if _, err := tls.X509KeyPair(tlsCert, tlsKey); err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to create the TLS keypair") + message.Fatal(err, "Unable to create the TLS keypair") } secretTLS := &corev1.Secret{ @@ -53,17 +102,97 @@ func ReplaceTLSSecret(namespace string, name string) { ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "zarf", + }, }, Type: corev1.SecretTypeTLS, Data: map[string][]byte{}, } - secretTLS.Data[corev1.TLSCertKey] = []byte(tlsCert) - secretTLS.Data[corev1.TLSPrivateKeyKey] = []byte(tlsKey) + secretTLS.Data[corev1.TLSCertKey] = tlsCert + secretTLS.Data[corev1.TLSPrivateKeyKey] = tlsKey + + return secretTLS +} + +func ReplaceTLSSecret(namespace string, name string) { + message.Debugf("k8s.ReplaceTLSSecret(%s, %s)", namespace, name) - _, err = namespaceSecrets.Create(context.TODO(), secretTLS, metav1.CreateOptions{}) + tlsCert, err := readFile(config.TLS.CertPublicPath) if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to create the secret", err) + message.Fatalf(err, "Unable to read the TLS public certificate") + } + tlsKey, err := readFile(config.TLS.CertPrivatePath) + if err != nil { + message.Fatalf(err, "Unable to read the TLS private key") + } + if _, err := tls.X509KeyPair(tlsCert, tlsKey); err != nil { + message.Fatalf(err, "Unable to create the TLS keypair") } + + secret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "zarf", + }, + }, + Type: corev1.SecretTypeTLS, + Data: map[string][]byte{}, + } + + secret.Data[corev1.TLSCertKey] = tlsCert + secret.Data[corev1.TLSPrivateKeyKey] = tlsKey + + if err := ReplaceSecret(secret); err != nil { + message.Fatalf(err, "Unable to create the secret") + } +} + +func ReplaceSecret(secret *corev1.Secret) error { + message.Debugf("k8s.ReplaceSecret(%v)", secret) + + if _, err := CreateNamespace(secret.Namespace, nil); err != nil { + return fmt.Errorf("unable to create or read the namespace: %w", err) + } + + if err := DeleteSecret(secret); err != nil { + return err + } + + return CreateSecret(secret) +} + +func DeleteSecret(secret *corev1.Secret) error { + message.Debugf("k8s.DeleteSecret(%v)", secret) + clientSet := getClientset() + + namespaceSecrets := clientSet.CoreV1().Secrets(secret.Namespace) + + err := namespaceSecrets.Delete(context.TODO(), secret.Name, metav1.DeleteOptions{}) + if err != nil && !errors.IsNotFound(err) { + return fmt.Errorf("error deleting the secret: %w", err) + } + + return nil +} + +func CreateSecret(secret *corev1.Secret) error { + message.Debugf("k8s.CreateSecret(%v)", secret) + clientSet := getClientset() + + namespaceSecrets := clientSet.CoreV1().Secrets(secret.Namespace) + + // create the given secret + if _, err := namespaceSecrets.Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil { + return fmt.Errorf("unable to create the secret: %w", err) + } + + return nil } diff --git a/cli/internal/k8s/services.go b/cli/internal/k8s/services.go new file mode 100644 index 0000000000..a98e9ff42a --- /dev/null +++ b/cli/internal/k8s/services.go @@ -0,0 +1,49 @@ +package k8s + +import ( + "context" + + "github.com/defenseunicorns/zarf/cli/internal/message" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// GetService returns a Kubernetes service resource in the provided namespace with the given name. +func GetService(namespace string, serviceName string) (*corev1.Service, error) { + message.Debugf("k8s.GetService(%s, %s)", namespace, serviceName) + clientset := getClientset() + return clientset.CoreV1().Services(namespace).Get(context.Background(), serviceName, metav1.GetOptions{}) +} + +// GetServicesByLabel returns a list of matched services given a label and value. To search all namespaces, pass "" in the namespace arg +func GetServicesByLabel(namespace string, label string, value string) (*corev1.ServiceList, error) { + message.Debugf("k8s.GetServicesByLabel(%s, %s)", namespace, label) + clientset := getClientset() + + // Creat the selector and add the requirement + labelSelector, _ := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{ + MatchLabels: map[string]string{ + label: value, + }, + }) + + // Run the query with the selector and return as a ServiceList + return clientset.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector.String()}) +} + +// GetServicesByLabelExists returns a list of matched services given a label. To search all namespaces, pass "" in the namespace arg +func GetServicesByLabelExists(namespace string, label string) (*corev1.ServiceList, error) { + message.Debugf("k8s.GetServicesByLabelExists(%s, %s)", namespace, label) + clientset := getClientset() + + // Creat the selector and add the requirement + labelSelector, _ := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: label, + Operator: metav1.LabelSelectorOpExists, + }}, + }) + + // Run the query with the selector and return as a ServiceList + return clientset.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector.String()}) +} diff --git a/cli/internal/k8s/state.go b/cli/internal/k8s/state.go new file mode 100644 index 0000000000..177b03ceea --- /dev/null +++ b/cli/internal/k8s/state.go @@ -0,0 +1,91 @@ +package k8s + +import ( + "context" + "encoding/json" + "fmt" + "github.com/defenseunicorns/zarf/cli/types" + + "github.com/defenseunicorns/zarf/cli/internal/message" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" +) + +const ( + ZarfNamespace = "zarf" + ZarfStateSecretName = "zarf-state" + ZarfStateDataKey = "state" +) + +// getZarfStateInterface returns a secret interface for the zarf namespace +func getZarfStateInterface() v1.SecretInterface { + message.Debug("k8s.getZarfStateInterface()") + clientSet := getClientset() + + // Get interface for all secrets in the zarf namespace + return clientSet.CoreV1().Secrets(ZarfNamespace) +} + +// LoadZarfState returns the current zarf/zarf-state secret data or an empty ZarfState +func LoadZarfState() types.ZarfState { + message.Debug("k8s.LoadZarfState()") + + // The empty state that we will try to fill + state := types.ZarfState{ + Distro: DistroIsUnknown, + } + + // Set up the API connection + secretInterface := getZarfStateInterface() + + // Try to get the zarf-state secret + if match, err := secretInterface.Get(context.TODO(), ZarfStateSecretName, metav1.GetOptions{}); err == nil { + _ = json.Unmarshal(match.Data[ZarfStateDataKey], &state) + } + + message.Debug(state) + + return state +} + +// SaveZarfState takes a given state and makepersists it to the zarf/zarf-state secret +func SaveZarfState(state types.ZarfState) error { + message.Debugf("k8s.SaveZarfState(%v)", state) + + // Convert the data back to JSON + data, err := json.Marshal(state) + if err != nil { + return fmt.Errorf("unable to json-encode the zarf state") + } + + // Set up the data wrapper + dataWrapper := make(map[string][]byte) + dataWrapper[ZarfStateDataKey] = data + + // The secret object + secret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: ZarfStateSecretName, + Namespace: ZarfNamespace, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "zarf", + }, + }, + Type: corev1.SecretTypeOpaque, + Data: dataWrapper, + } + + message.Debug(secret) + + // Attempt to create or replace the secret and return + if err := ReplaceSecret(secret); err != nil { + return fmt.Errorf("unable to create the zarf state secret") + } + + return nil +} diff --git a/cli/internal/k8s/tunnel.go b/cli/internal/k8s/tunnel.go new file mode 100644 index 0000000000..2c3ff60d39 --- /dev/null +++ b/cli/internal/k8s/tunnel.go @@ -0,0 +1,327 @@ +package k8s + +// Forked from https://github.com/gruntwork-io/terratest/blob/v0.38.8/modules/k8s/tunnel.go + +import ( + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "os" + "os/exec" + "os/signal" + "runtime" + "strconv" + "strings" + "sync" + "syscall" + + "github.com/defenseunicorns/zarf/cli/types" + + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/message" + "k8s.io/client-go/tools/portforward" + "k8s.io/client-go/transport/spdy" +) + +// Global lock to synchronize port selections +var globalMutex sync.Mutex + +const ( + PodResource = "pod" + SvcResource = "svc" + ZarfRegistry = "REGISTRY" + ZarfLogging = "LOGGING" + ZarfGit = "GIT" +) + +const ( + PortRegistry = iota + 45001 + PortLogging + PortGit +) + +// makeLabels is a helper to format a map of label key and value pairs into a single string for use as a selector. +func makeLabels(labels map[string]string) string { + var out []string + for key, value := range labels { + out = append(out, fmt.Sprintf("%s=%s", key, value)) + } + return strings.Join(out, ",") +} + +// Tunnel is the main struct that configures and manages port forwading tunnels to Kubernetes resources. +type Tunnel struct { + out io.Writer + localPort int + remotePort int + namespace string + resourceType string + resourceName string + urlSuffix string + stopChan chan struct{} + readyChan chan struct{} +} + +// NewTunnel will create a new Tunnel struct +// Note that if you use 0 for the local port, an open port on the host system +// will be selected automatically, and the Tunnel struct will be updated with the selected port. +func NewTunnel(namespace string, resourceType string, resourceName string, local int, remote int) *Tunnel { + message.Debugf("tunnel.NewTunnel(%s, %s, %s, %v, %v)", namespace, resourceType, resourceName, local, remote) + return &Tunnel{ + out: ioutil.Discard, + localPort: local, + remotePort: remote, + namespace: namespace, + resourceType: resourceType, + resourceName: resourceName, + stopChan: make(chan struct{}, 1), + readyChan: make(chan struct{}, 1), + } +} + +func NewZarfTunnel() *Tunnel { + return NewTunnel(ZarfNamespace, SvcResource, "", 0, 0) +} + +func (tunnel *Tunnel) Connect(target string, blocking bool) { + message.Debugf("tunnel.Connect(%s, %v)", target, blocking) + switch strings.ToUpper(target) { + case ZarfRegistry: + tunnel.resourceName = "zarf-docker-registry" + tunnel.localPort = PortRegistry + tunnel.remotePort = 5000 + case ZarfLogging: + tunnel.resourceName = "zarf-loki-stack-grafana" + tunnel.localPort = PortLogging + tunnel.remotePort = 3000 + case ZarfGit: + tunnel.resourceName = "zarf-gitea-http" + tunnel.localPort = PortGit + tunnel.remotePort = 3000 + default: + if target != "" { + if err := tunnel.checkForZarfConnectLabel(target); err != nil { + message.Errorf(err, "Problem looking for a zarf connect label in the cluster") + } + } + + if tunnel.resourceName == "" { + message.Fatalf(nil, "Ensure a resource name is provided") + } + if tunnel.remotePort < 1 { + message.Fatal(nil, "A remote port must be specified to connect to.") + } + } + + if url, err := tunnel.Establish(); err != nil { + // On error abbort + message.Fatal(err, "Unable to establish the tunnel") + } else if blocking { + // Otherwise, if this is blocking it is coming from a user request so try to open the URL, but ignore errors + switch runtime.GOOS { + case "linux": + _ = exec.Command("xdg-open", url).Start() + case "windows": + _ = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() + case "darwin": + _ = exec.Command("open", url).Start() + } + + // Since this blocking, set the defer now so it closes properly on sigterm + defer tunnel.Close() + + // Keep this open until an interrupt signal is received + c := make(chan os.Signal) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + go func() { + <-c + os.Exit(0) + }() + + for { + runtime.Gosched() + } + } +} + +// Endpoint returns the tunnel endpoint +func (tunnel *Tunnel) Endpoint() string { + message.Debug("tunnel.Endpoint()") + return fmt.Sprintf("localhost:%d", tunnel.localPort) +} + +// Close disconnects a tunnel connection by closing the StopChan, thereby stopping the goroutine. +func (tunnel *Tunnel) Close() { + message.Debug("tunnel.Close()") + close(tunnel.stopChan) +} + +func (tunnel *Tunnel) checkForZarfConnectLabel(name string) error { + message.Debugf("tunnel.checkForZarfConnectLabel(%s)", name) + matches, err := GetServicesByLabel("", config.ZarfConnectLabelName, name) + if err != nil { + return fmt.Errorf("unable to lookup the service: %w", err) + } + + if len(matches.Items) > 0 { + // If there is a match, use the first one as these are supposed to be unique + svc := matches.Items[0] + + // Reset based on the matched params + tunnel.resourceType = SvcResource + tunnel.resourceName = svc.Name + tunnel.namespace = svc.Namespace + // Only support a service with a single port + tunnel.remotePort = svc.Spec.Ports[0].TargetPort.IntValue() + + // Add the url suffix too + tunnel.urlSuffix = svc.Annotations[config.ZarfConnectAnnotationUrl] + + message.Debugf("tunnel connection match: %s/%s on port %d", svc.Namespace, svc.Name, tunnel.remotePort) + } + + return nil +} + +// Establish opens a tunnel to a kubernetes resource, as specified by the provided tunnel struct. +func (tunnel *Tunnel) Establish() (string, error) { + message.Debug("tunnel.Establish()") + spinner := message.NewProgressSpinner("Creating a port forwarding tunnel for resource %s/%s in namespace %s routing local port %d to remote port %d", + tunnel.resourceType, + tunnel.resourceName, + tunnel.namespace, + tunnel.localPort, + tunnel.remotePort, + ) + defer spinner.Stop() + + // Find the pod to port forward to + podName, err := tunnel.getAttachablePodForResource() + if err != nil { + return "", fmt.Errorf("unable to find pod attached to given resource: %w", err) + } + spinner.Debugf("Selected pod %s to open port forward to", podName) + + clientset := getClientset() + + // Build url to the port forward endpoint + // example: http://localhost:8080/api/v1/namespaces/helm/pods/tiller-deploy-9itlq/portforward + postEndpoint := clientset.CoreV1().RESTClient().Post() + namespace := tunnel.namespace + portForwardCreateURL := postEndpoint. + Resource("pods"). + Namespace(namespace). + Name(podName). + SubResource("portforward"). + URL() + + spinner.Debugf("Using URL %s to create portforward", portForwardCreateURL) + + restConfig := getRestConfig() + + // Construct the spdy client required by the client-go portforward library + transport, upgrader, err := spdy.RoundTripperFor(restConfig) + if err != nil { + return "", fmt.Errorf("unable to create the spdy client %w", err) + } + dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", portForwardCreateURL) + + // If the local-port is 0, get an available port before continuing. We do this here instead of relying on the + // underlying port-forwarder library, because the port-forwarder library does not expose the selected local port in a + // machine-readable manner. + // Synchronize on the global lock to avoid race conditions with concurrently selecting the same available port, + // since there is a brief moment between `GetAvailablePort` and `forwarder.ForwardPorts` where the selected port + // is available for selection again. + if tunnel.localPort == 0 { + spinner.Debugf("Requested local port is 0. Selecting an open port on host system") + tunnel.localPort, err = GetAvailablePort() + if err != nil { + return "", fmt.Errorf("unable to find an available port: %w", err) + } + spinner.Debugf("Selected port %d", tunnel.localPort) + globalMutex.Lock() + defer globalMutex.Unlock() + } + + // Construct a new PortForwarder struct that manages the instructed port forward tunnel + ports := []string{fmt.Sprintf("%d:%d", tunnel.localPort, tunnel.remotePort)} + portforwarder, err := portforward.New(dialer, ports, tunnel.stopChan, tunnel.readyChan, tunnel.out, tunnel.out) + if err != nil { + return "", fmt.Errorf("unable to create the port forward: %w", err) + } + + // Open the tunnel in a goroutine so that it is available in the background. Report errors to the main goroutine via + // a new channel. + errChan := make(chan error) + go func() { + errChan <- portforwarder.ForwardPorts() + }() + + // Wait for an error or the tunnel to be ready + select { + case err = <-errChan: + spinner.Stop() + return "", fmt.Errorf("unable to start the tunnel: %w", err) + case <-portforwarder.Ready: + url := fmt.Sprintf("http://%s:%v%s", config.IPV4Localhost, tunnel.localPort, tunnel.urlSuffix) + spinner.Successf("Creating port forwarding tunnel available at %s", url) + return url, nil + } +} + +// GetAvailablePort retrieves an available port on the host machine. This delegates the port selection to the golang net +// library by starting a server and then checking the port that the server is using. +func GetAvailablePort() (int, error) { + message.Debug("tunnel.GetAvailablePort()") + l, err := net.Listen("tcp", ":0") + if err != nil { + return 0, err + } + defer func(l net.Listener) { + // ignore this error because it won't help us to tell the user + _ = l.Close() + }(l) + + _, p, err := net.SplitHostPort(l.Addr().String()) + if err != nil { + return 0, err + } + port, err := strconv.Atoi(p) + if err != nil { + return 0, err + } + return port, err +} + +// getAttachablePodForResource will find a pod that can be port forwarded to the provided resource type and return +// the name. +func (tunnel *Tunnel) getAttachablePodForResource() (string, error) { + message.Debug("tunnel.GettAttachablePodForResource()") + switch tunnel.resourceType { + case PodResource: + return tunnel.resourceName, nil + case SvcResource: + return tunnel.getAttachablePodForService() + default: + return "", fmt.Errorf("unknown resource type: %s", tunnel.resourceType) + } +} + +// getAttachablePodForServiceE will find an active pod associated with the Service and return the pod name. +func (tunnel *Tunnel) getAttachablePodForService() (string, error) { + message.Debug("tunnel.getAttachablePodForService()") + service, err := GetService(tunnel.namespace, tunnel.resourceName) + if err != nil { + return "", fmt.Errorf("unable to find the service: %w", err) + } + selectorLabelsOfPods := makeLabels(service.Spec.Selector) + + servicePods := WaitForPodsAndContainers(types.ZarfContainerTarget{ + Namespace: tunnel.namespace, + Selector: selectorLabelsOfPods, + }, false) + + return servicePods[0], nil +} diff --git a/cli/internal/kustomize/build.go b/cli/internal/kustomize/build.go new file mode 100644 index 0000000000..03548fcf27 --- /dev/null +++ b/cli/internal/kustomize/build.go @@ -0,0 +1,28 @@ +package kustomize + +import ( + "fmt" + + "github.com/defenseunicorns/zarf/cli/internal/utils" + "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +// BuildKustomization reads a kustomization and builds it into a single yaml file +func BuildKustomization(path string, destination string) error { + // Kustomize has to write to the filesystem on-disk + fSys := filesys.MakeFsOnDisk() + kustomizer := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) + + // Try to build the kustomization + resources, err := kustomizer.Run(fSys, path) + if err != nil { + return err + } + + if yaml, err := resources.AsYaml(); err != nil { + return fmt.Errorf("problem converting kustomization to yaml: %w", err) + } else { + return utils.WriteFile(destination, yaml) + } +} diff --git a/cli/internal/message/message.go b/cli/internal/message/message.go new file mode 100644 index 0000000000..0545543e09 --- /dev/null +++ b/cli/internal/message/message.go @@ -0,0 +1,140 @@ +package message + +import ( + "fmt" + "os" + "strings" + + "github.com/pterm/pterm" +) + +type LogLevel int + +const ( + // WarnLevel level. Non-critical entries that deserve eyes. + WarnLevel LogLevel = iota + // InfoLevel level. General operational entries about what's going on inside the + // application. + InfoLevel + // DebugLevel level. Usually only enabled when debugging. Very verbose logging. + DebugLevel + // TraceLevel level. Designates finer-grained informational events than the Debug. + TraceLevel +) + +var logLevel = InfoLevel + +func init() { + // Help capture text cleaner + pterm.SetDefaultOutput(os.Stderr) + pterm.ThemeDefault.SuccessMessageStyle = *pterm.NewStyle(pterm.FgLightGreen) + // Customize default error. + pterm.Success.Prefix = pterm.Prefix{ + Text: " ✔", + Style: pterm.NewStyle(pterm.FgLightGreen), + } + pterm.Error.Prefix = pterm.Prefix{ + Text: " ERROR:", + Style: pterm.NewStyle(pterm.BgLightRed, pterm.FgBlack), + } + pterm.Info.Prefix = pterm.Prefix{ + Text: " •", + } +} + +func debugPrinter() *pterm.PrefixPrinter { + return pterm.Debug.WithShowLineNumber(logLevel > 2).WithLineNumberOffset(2) +} + +func errorPrinter() *pterm.PrefixPrinter { + return pterm.Error.WithShowLineNumber(logLevel > 2).WithLineNumberOffset(2) +} + +func SetLogLevel(lvl LogLevel) { + logLevel = lvl + if logLevel >= DebugLevel { + pterm.EnableDebugMessages() + } +} + +func GetLogLevel() LogLevel { + return logLevel +} + +func Debug(payload ...interface{}) { + debugPrinter().Println(payload...) +} + +func Debugf(format string, a ...interface{}) { + debugPrinter().Printfln(format, a...) +} + +func Error(err interface{}, message string) { + Errorf(err, message) +} + +func Errorf(err interface{}, format string, a ...interface{}) { + Debug(err) + Warnf(format, a...) +} + +func Warn(message string) { + Warnf(message) +} + +func Warnf(format string, a ...interface{}) { + message := paragraph(format, a...) + pterm.Warning.Println(message) +} + +func Fatal(err interface{}, message string) { + Debug(err) + errorPrinter().Println(message) + os.Exit(1) +} + +func Fatalf(err interface{}, format string, a ...interface{}) { + Debug(err) + message := paragraph(format, a...) + errorPrinter().Println(message) + os.Exit(1) +} + +func Info(message string) { + Infof(message) +} + +func Infof(format string, a ...interface{}) { + if logLevel > 0 { + message := paragraph(format, a...) + pterm.Info.Println(message) + } +} + +func Question(text string) { + pterm.Println() + message := paragraph(text) + pterm.FgMagenta.Println(message) +} + +func Note(text string) { + pterm.Println() + message := paragraph(text) + pterm.FgYellow.Println(message) +} + +func HeaderInfof(format string, a ...interface{}) { + message := fmt.Sprintf(format, a...) + // Ensure the text is consistent for the header width + padding := 85 - len(message) + pterm.Println() + pterm.DefaultHeader. + WithBackgroundStyle(pterm.NewStyle(pterm.BgDarkGray)). + WithTextStyle(pterm.NewStyle(pterm.FgLightWhite)). + WithMargin(2). + Printfln(message + strings.Repeat(" ", padding)) +} + +func paragraph(format string, a ...interface{}) string { + return pterm.DefaultParagraph.WithMaxWidth(100).Sprintf(format, a...) +} diff --git a/cli/internal/message/spinner.go b/cli/internal/message/spinner.go new file mode 100644 index 0000000000..d89723706d --- /dev/null +++ b/cli/internal/message/spinner.go @@ -0,0 +1,75 @@ +package message + +import ( + "fmt" + "github.com/pterm/pterm" +) + +type Spinner struct { + spinner *pterm.SpinnerPrinter + startText string +} + +func NewProgressSpinner(format string, a ...interface{}) *Spinner { + text := fmt.Sprintf(format, a...) + spinner, _ := pterm.DefaultSpinner. + WithRemoveWhenDone(false). + // Src: https://github.com/gernest/wow/blob/master/spin/spinners.go#L335 + WithSequence(` ⠋ `, ` ⠙ `, ` ⠹ `, ` ⠸ `, ` ⠼ `, ` ⠴ `, ` ⠦ `, ` ⠧ `, ` ⠇ `, ` ⠏ `). + Start(text) + + return &Spinner{ + spinner: spinner, + startText: text, + } +} + +func (p *Spinner) Write(text []byte) (int, error) { + Debug(string(text)) + return len(text), nil +} + +func (p *Spinner) Updatef(format string, a ...interface{}) { + text := fmt.Sprintf(format, a...) + p.spinner.UpdateText(text) +} + +func (p *Spinner) Debugf(format string, a ...interface{}) { + if logLevel >= DebugLevel { + text := fmt.Sprintf("Debug: "+format, a...) + p.spinner.UpdateText(text) + } +} + +func (p *Spinner) Stop() { + if p.spinner.IsActive { + // Only stop if not stopped to avoid extra line break injections in the CLI + _ = p.spinner.Stop() + Debug("Possible spinner leak detected") + } +} + +func (p *Spinner) Success() { + p.Successf(p.startText) +} + +func (p *Spinner) Successf(format string, a ...interface{}) { + text := fmt.Sprintf(format, a...) + p.spinner.Success(text) +} + +func (p *Spinner) Warnf(format string, a ...interface{}) { + text := fmt.Sprintf(format, a...) + p.spinner.Warning(text) +} + +func (p *Spinner) Errorf(err error, format string, a ...interface{}) { + p.Warnf(format, a...) + Debug(err) +} + +func (p *Spinner) Fatalf(err error, format string, a ...interface{}) { + p.spinner.RemoveWhenDone = true + p.spinner.Stop() + Fatalf(err, format, a...) +} diff --git a/cli/internal/message/tls/prompts.go b/cli/internal/message/tls/prompts.go new file mode 100644 index 0000000000..180b844ab2 --- /dev/null +++ b/cli/internal/message/tls/prompts.go @@ -0,0 +1,137 @@ +package tls + +import ( + "net" + "os" + "path/filepath" + + "github.com/AlecAivazis/survey/v2" + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/internal/utils" +) + +const InvalidHostMessage = "The hostname provided (%s) was not a valid hostname. The hostname can only contain: 'a-z', 'A-Z', '0-9', '-', and '.' characters as defined by RFC-1035. If using localhost, you must use the 127.0.0.1.\n" + +// HasCertPaths Check for cert paths provided via automation (both required) +func HasCertPaths() bool { + return config.TLS.CertPrivatePath != "" && config.TLS.CertPublicPath != "" +} + +// PromptIsImportCerts Ask user if they will be importing or generating certs, return true if importing certs +func PromptIsImportCerts(confirmed bool) bool { + var mode int + + if HasCertPaths() { + return true + } + + if confirmed { + // Assume generate on confirmed without cert paths + return false + } + + message.Question(` + Zarf needs a valid TLS certificate and key to serve content. This can be automatically generated + for you, but will require you to provide the generated certificate authority public key to any + systems that will connect to this cluster. Failure to do so may generating a warning for users or + fail to connect to the cluster. You can also provide your own X509 certificates instead.`) + + // Determine flow for generate or import + modePrompt := &survey.Select{ + Message: "Will Zarf be generating a TLS chain or importing an existing ingress cert?", + Options: []string{ + "Generate TLS chain with an ephemeral CA", + "Import user-provided cert keypair", + }, + } + _ = survey.AskOne(modePrompt, &mode) + + return mode == 1 +} + +// PromptCertPaths Ask user for the public and private key paths to import into the cluster +func PromptCertPaths() { + prompt := &survey.Input{ + Message: "Enter a file path to the ingress public key", + Suggest: func(toComplete string) []string { + // Give some suggestions to users + files, _ := filepath.Glob(toComplete + "*") + return files + }, + } + _ = survey.AskOne(prompt, &config.TLS.CertPublicPath, survey.WithValidator(survey.Required)) + + prompt.Message = "Enter a file path to the ingress private key" + _ = survey.AskOne(prompt, &config.TLS.CertPrivatePath, survey.WithValidator(survey.Required)) +} + +// PromptAndValidateHost Ask user for the hostname or ip if not provided via automation and validate the input +func PromptAndValidateHost(confirmed bool) { + if config.TLS.Host == "" { + if confirmed { + // Fail if host is not provided on confirm + message.Fatalf(nil, InvalidHostMessage, config.TLS.Host) + } + + message.Question(` + Zarf needs to know what static IP address or DNS name will be exposed for traffic + routed into the cluster. This will be how you connect to the cluster and if importing a + certificate should match the Subject Alternate Name specified in that certificate.`) + + message.Note(" Note: if using localhost, be sure to choose " + config.IPV4Localhost) + + // If not provided, always ask for a host entry to avoid having to guess which entry in a cert if provided + prompt := &survey.Input{ + Message: "What IP address or DNS name do you want to use?", + Suggest: func(toComplete string) []string { + var suggestions []string + // Create a list of IPs to add to the suggestion box + interfaces, err := net.InterfaceAddrs() + if err == nil { + for _, iface := range interfaces { + // Convert the CIDR to the IP string if valid + ip, _, _ := net.ParseCIDR(iface.String()) + if utils.ValidHostname(ip.String()) { + suggestions = append(suggestions, ip.String()) + } + } + } + // Add the localhost hostname as well + hostname, _ := os.Hostname() + if hostname != "" { + suggestions = append(suggestions, hostname) + } + + return suggestions + }, + } + err := survey.AskOne(prompt, &config.TLS.Host, survey.WithValidator(survey.Required)) + if err != nil && err.Error() == os.Interrupt.String() { + // Handle CTRL+C + os.Exit(0) + } + } + + if !utils.ValidHostname(config.TLS.Host) { + // When hitting an invalid hostname... + if confirmed { + // ...if using automation end it all + message.Fatalf(nil, InvalidHostMessage, config.TLS.Host) + } + // ...otherwise, warn user, reset the field, and cycle the function + message.Fatalf(nil, InvalidHostMessage, config.TLS.Host) + config.TLS.Host = "" + PromptAndValidateHost(confirmed) + } +} + +func HandleTLSOptions(confirmed bool) { + // Get and validate host + PromptAndValidateHost(confirmed) + + // Get the cert path if this is an import + if PromptIsImportCerts(confirmed) && !HasCertPaths() { + PromptCertPaths() + } +} diff --git a/cli/internal/packager/common.go b/cli/internal/packager/common.go index 2b3e4b9836..a27401a4ae 100644 --- a/cli/internal/packager/common.go +++ b/cli/internal/packager/common.go @@ -3,6 +3,8 @@ package packager import ( "crypto/sha256" "encoding/hex" + "fmt" + "github.com/defenseunicorns/zarf/cli/types" "io" "io/ioutil" "net/http" @@ -11,57 +13,63 @@ import ( "strings" "time" + "github.com/goccy/go-yaml" + "github.com/AlecAivazis/survey/v2" "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/defenseunicorns/zarf/cli/internal/utils" - "github.com/sirupsen/logrus" ) type componentPaths struct { base string files string charts string - images string + values string repos string manifests string } type tempPaths struct { base string + seedImages string + images string dataInjections string components string } func createPaths() tempPaths { - basePath := utils.MakeTempDir() + basePath, _ := utils.MakeTempDir() return tempPaths{ base: basePath, + seedImages: basePath + "/seed-images.tar", + images: basePath + "/images.tar", dataInjections: basePath + "/data", components: basePath + "/components", } } -func createComponentPaths(basePath string, component config.ZarfComponent) componentPaths { +func (t tempPaths) clean() { + message.Debug("Cleaning up temp files") + _ = os.RemoveAll(t.base) +} + +func createComponentPaths(basePath string, component types.ZarfComponent) componentPaths { basePath = basePath + "/" + component.Name _ = utils.CreateDirectory(basePath, 0700) return componentPaths{ base: basePath, files: basePath + "/files", charts: basePath + "/charts", - images: basePath + "/images-component-" + component.Name + ".tar", repos: basePath + "/repos", manifests: basePath + "/manifests", + values: basePath + "/values", } } -func cleanup(tempPath tempPaths) { - logrus.Info("Cleaning up temp files") - _ = os.RemoveAll(tempPath.base) -} - -func confirmAction(configPath string, confirm bool, message string) bool { +func confirmAction(configPath string, userMessage string) bool { content, err := ioutil.ReadFile(configPath) if err != nil { - logrus.Fatal(err) + message.Fatal(err, "Unable to open the package config file") } // Convert []byte to string and print to screen @@ -70,20 +78,20 @@ func confirmAction(configPath string, confirm bool, message string) bool { utils.ColorPrintYAML(text) // Display prompt if not auto-confirmed - if confirm { - logrus.Info(message + " Zarf package confirmed") + if config.DeployOptions.Confirm { + message.Infof("%s Zarf package confirmed", userMessage) } else { prompt := &survey.Confirm{ - Message: message + " this Zarf package?", + Message: userMessage + " this Zarf package?", } - _ = survey.AskOne(prompt, &confirm) + _ = survey.AskOne(prompt, &config.DeployOptions.Confirm) } - return confirm + return config.DeployOptions.Confirm } -func getValidComponents(allComponents []config.ZarfComponent, requestedComponentNames []string) []config.ZarfComponent { - var validComponentsList []config.ZarfComponent +func getValidComponents(allComponents []types.ZarfComponent, requestedComponentNames []string) []types.ZarfComponent { + var validComponentsList []types.ZarfComponent confirmedComponents := make([]bool, len(requestedComponentNames)) for _, component := range allComponents { confirmComponent := component.Required @@ -99,11 +107,17 @@ func getValidComponents(allComponents []config.ZarfComponent, requestedComponent } } } else { + // Present the users with the component details one more time + displayComponent := component + displayComponent.Description = "" + content, _ := yaml.Marshal(displayComponent) + utils.ColorPrintYAML(string(content)) + message.Question(fmt.Sprintf("%s: %s", component.Name, component.Description)) + // Since no requested components were provided, prompt the user prompt := &survey.Confirm{ - Message: "Deploy the " + component.Name + " component?", + Message: "Deploy this component?", Default: component.Default, - Help: component.Description, } _ = survey.AskOne(prompt, &confirmComponent) } @@ -111,10 +125,14 @@ func getValidComponents(allComponents []config.ZarfComponent, requestedComponent if confirmComponent { validComponentsList = append(validComponentsList, component) + // Make it easier to know we are running k3s + if config.IsZarfInitConfig() && component.Name == "k3s" { + config.DeployOptions.ApplianceMode = true + } } } - // Verify that we were able to successfully identify all of the requested components + // Verify that we were able to successfully identify all the requested components var nonMatchedComponents []string for requestedComponentIndex, componentMatched := range confirmedComponents { if !componentMatched { @@ -123,46 +141,45 @@ func getValidComponents(allComponents []config.ZarfComponent, requestedComponent } if len(nonMatchedComponents) > 0 { - logrus.Fatalf("Unable to find these components to deploy: %v.", nonMatchedComponents) + message.Fatalf(nil, "Unable to find these components to deploy: %v.", nonMatchedComponents) } return validComponentsList } // HandleIfURL If provided package is a URL download it to a temp directory -func HandleIfURL(packagePath string, shasum string, insecureDeploy bool) string { +func HandleIfURL(packagePath string, shasum string, insecureDeploy bool) (string, func()) { // Check if the user gave us a remote package providedURL, err := url.Parse(packagePath) if err != nil || providedURL.Scheme == "" || providedURL.Host == "" { - logrus.WithField("archive", packagePath).Debug("The package provided is not a remote package.") - return packagePath + return packagePath, func() {} } if !insecureDeploy && shasum == "" { - logrus.Fatal("When deploying a remote package you must provide either a `--shasum` or the `--insecure` flag. Neither were provided.") + message.Fatal(nil, "When deploying a remote package you must provide either a `--shasum` or the `--insecure` flag. Neither were provided.") } // Check the extension on the package is what we expect if !isValidFileExtension(providedURL.Path) { - logrus.Fatalf("Only %s file extensions are permitted.\n", config.GetValidPackageExtensions()) + message.Fatalf(nil, "Only %s file extensions are permitted.\n", config.GetValidPackageExtensions()) } // Download the package resp, err := http.Get(packagePath) if err != nil { - logrus.Fatal("Unable to download the package: ", err) + message.Fatal(err, "Unable to download the package") } defer resp.Body.Close() // Write the package to a local file tempPath := createPaths() + localPackagePath := tempPath.base + providedURL.Path - logrus.Debug("Creating local package with the path: ", localPackagePath) + message.Debugf("Creating local package with the path: %s", localPackagePath) packageFile, _ := os.Create(localPackagePath) _, err = io.Copy(packageFile, resp.Body) if err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to copy the contents of the provided URL into a local file.") + message.Fatal(err, "Unable to copy the contents of the provided URL into a local file.") } // Check the shasum if necessary @@ -170,24 +187,22 @@ func HandleIfURL(packagePath string, shasum string, insecureDeploy bool) string hasher := sha256.New() _, err = io.Copy(hasher, packageFile) if err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to calculate the sha256 of the provided remote package.") + message.Fatal(err, "Unable to calculate the sha256 of the provided remote package.") } value := hex.EncodeToString(hasher.Sum(nil)) if value != shasum { _ = os.Remove(localPackagePath) - logrus.Fatalf("Provided shasum (%s) of the package did not match what was downloaded (%s)\n", shasum, value) + message.Fatalf(nil, "Provided shasum (%s) of the package did not match what was downloaded (%s)\n", shasum, value) } } - return localPackagePath + return localPackagePath, tempPath.clean } func isValidFileExtension(filename string) bool { for _, extension := range config.GetValidPackageExtensions() { if strings.HasSuffix(filename, extension) { - logrus.WithField("packagePath", filename).Warn("Package extension is valid.") return true } } @@ -196,54 +211,66 @@ func isValidFileExtension(filename string) bool { } func loopScriptUntilSuccess(script string, retry bool) { - logContext := logrus.WithField("script", script) - logContext.Info("Waiting for script to complete successfully") - - var output string - var err error + spinner := message.NewProgressSpinner("Waiting for command \"%s\"", script) + defer spinner.Stop() // Try to patch the zarf binary path in case the name isn't exactly "./zarf" binaryPath, err := os.Executable() if err != nil { - logContext.Debug(err) - logContext.Warn("Unable to determine the current zarf binary path") + spinner.Errorf(err, "Unable to determine the current zarf binary path") } else { script = strings.ReplaceAll(script, "./zarf ", binaryPath+" ") - // Update since we may have a new parsed script - logContext = logrus.WithField("script", script) } // 2 minutes per script (60 * 2 second waits) tries := 60 for { - tries-- - // If there are no more tries left, drop a warning and continue - if tries < 1 { - logContext.Warn("Script failed or timed out") - logContext.Print(output) - break - } scriptEnvVars := []string{ - "ZARF_TARGET_ENDPOINT=" + config.GetTargetEndpoint(), + "ZARF_REGISTRY=" + config.ZarfRegistry, + "ZARF_SEED_REGISTRY=" + config.ZarfLocalSeedRegistry, } // Try to silently run the script - output, err = utils.ExecCommand(false, scriptEnvVars, "sh", "-c", script) + output, err := utils.ExecCommand(false, scriptEnvVars, "sh", "-c", script) + if err != nil { - logrus.Debug(err) + message.Debug(err, output) + if retry { - // if retry is enabled, on error wait 2 seconds and try again - time.Sleep(time.Second * 2) - } else { - // No retry, abort - tries = 0 - } - continue - } else { - // Script successful, output results and continue - if output != "" { - logContext.Print(output) + tries-- + + // If there are no more tries left, we have failed + if tries < 1 { + spinner.Fatalf(nil, "Script timed out after 2 minutes") + } else { + // if retry is enabled, on error wait 2 seconds and try again + time.Sleep(time.Second * 2) + continue + } } - break + + spinner.Fatalf(nil, "Script failed") } + + // Script successful,continue + message.Debug(output) + spinner.Success() + break + } +} + +// removeDuplicates reduces a string slice to unique values only, https://www.dotnetperls.com/duplicates-go +func removeDuplicates(elements []string) []string { + seen := map[string]bool{} + + // Create a map of all unique elements. + for v := range elements { + seen[elements[v]] = true + } + + // Place all keys from the map into a slice. + var result []string + for key := range seen { + result = append(result, key) } + return result } diff --git a/cli/internal/packager/create.go b/cli/internal/packager/create.go index fd4aaf1c65..5fd0b8ed77 100644 --- a/cli/internal/packager/create.go +++ b/cli/internal/packager/create.go @@ -1,49 +1,71 @@ package packager import ( + "fmt" "os" "path/filepath" "regexp" "strconv" + "strings" + + "github.com/defenseunicorns/zarf/cli/internal/kustomize" + "github.com/defenseunicorns/zarf/cli/internal/packager/validate" + "github.com/defenseunicorns/zarf/cli/types" "github.com/defenseunicorns/zarf/cli/config" "github.com/defenseunicorns/zarf/cli/internal/git" "github.com/defenseunicorns/zarf/cli/internal/helm" "github.com/defenseunicorns/zarf/cli/internal/images" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/defenseunicorns/zarf/cli/internal/utils" "github.com/mholt/archiver/v3" - "github.com/sirupsen/logrus" ) -func Create(confirm bool) { - +// Create generates a zarf package tarball for consumption by +func Create() { if err := config.LoadConfig("zarf.yaml"); err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to read the zarf.yaml file") + message.Fatal(err, "Unable to read the zarf.yaml file") } tempPath := createPaths() + defer tempPath.clean() + packageName := config.GetPackageName() dataInjections := config.GetDataInjections() + seedImages := config.GetSeedImages() components := config.GetComponents() configFile := tempPath.base + "/zarf.yaml" + config.SetAcrch() + // Save the transformed config if err := config.BuildConfig(configFile); err != nil { - logrus.Debug(err) - logrus.WithField("path", configFile).Fatal("Unable to write the zarf.yaml file") + message.Fatalf(err, "Unable to write the %s file", configFile) } - confirm = confirmAction(configFile, confirm, "Create") + // Perform early package validation + validate.Run() - if !confirm { + if !confirmAction(configFile, "Create") { os.Exit(0) } + if len(seedImages) > 0 { + // Load seed images into their own happy little tarball for ease of import on init + images.PullAll(seedImages, tempPath.seedImages) + } + + var combinedImageList []string for _, component := range components { - logrus.WithField("component", component.Name).Info("Loading component assets") - componentPath := createComponentPaths(tempPath.components, component) - addLocalAssets(componentPath, component) + addComponent(tempPath, component) + // Combine all component images into a single entry for efficient layer reuse + combinedImageList = append(combinedImageList, component.Images...) + } + + // Images are handled separately from other component assets + if len(combinedImageList) > 0 { + uniqueList := removeDuplicates(combinedImageList) + images.PullAll(uniqueList, tempPath.images) } if config.IsZarfInitConfig() { @@ -52,7 +74,6 @@ func Create(confirm bool) { } else { // Init packages do not use data or utilityCluster keys if len(dataInjections) > 0 { - logrus.Info("Loading data injections") for _, data := range dataInjections { destinationFile := tempPath.dataInjections + "/" + filepath.Base(data.Target.Path) utils.CreatePathAndCopy(data.Source, destinationFile) @@ -62,35 +83,37 @@ func Create(confirm bool) { _ = os.RemoveAll(packageName) err := archiver.Archive([]string{tempPath.base + "/"}, packageName) if err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to create the package archive") + message.Fatal(err, "Unable to create the package archive") } - - logrus.WithField("name", packageName).Info("Package creation complete") - - cleanup(tempPath) } -func addLocalAssets(tempPath componentPaths, assets config.ZarfComponent) { - if len(assets.Charts) > 0 { - logrus.Info("Loading static helm charts") - _ = utils.CreateDirectory(tempPath.charts, 0700) +func addComponent(tempPath tempPaths, component types.ZarfComponent) { + message.HeaderInfof("📦 %s COMPONENT", strings.ToUpper(component.Name)) + componentPath := createComponentPaths(tempPath.components, component) + + if len(component.Charts) > 0 { + _ = utils.CreateDirectory(componentPath.charts, 0700) + _ = utils.CreateDirectory(componentPath.values, 0700) re := regexp.MustCompile(`\.git$`) - for _, chart := range assets.Charts { - matched := re.MatchString(chart.Url) - if matched { - helm.DownloadChartFromGit(chart, tempPath.charts) + for _, chart := range component.Charts { + isGitURL := re.MatchString(chart.Url) + if isGitURL { + _ = helm.DownloadChartFromGit(chart, componentPath.charts) } else { - helm.DownloadPublishedChart(chart, tempPath.charts) + helm.DownloadPublishedChart(chart, componentPath.charts) + } + for idx, path := range chart.ValuesFiles { + chartValueName := helm.StandardName(componentPath.values, chart) + "-" + strconv.Itoa(idx) + utils.CreatePathAndCopy(path, chartValueName) } } } - if len(assets.Files) > 0 { - logrus.Info("Downloading files for local install") - _ = utils.CreateDirectory(tempPath.files, 0700) - for index, file := range assets.Files { - destinationFile := tempPath.files + "/" + strconv.Itoa(index) + if len(component.Files) > 0 { + _ = utils.CreateDirectory(componentPath.files, 0700) + for index, file := range component.Files { + message.Debugf("Loading %v", file) + destinationFile := componentPath.files + "/" + strconv.Itoa(index) if utils.IsUrl(file.Source) { utils.DownloadToFile(file.Source, destinationFile) } else { @@ -102,7 +125,9 @@ func addLocalAssets(tempPath componentPaths, assets config.ZarfComponent) { utils.ValidateSha256Sum(file.Shasum, destinationFile) } - if file.Executable { + info, _ := os.Stat(destinationFile) + + if file.Executable || info.IsDir() { _ = os.Chmod(destinationFile, 0700) } else { _ = os.Chmod(destinationFile, 0600) @@ -110,22 +135,42 @@ func addLocalAssets(tempPath componentPaths, assets config.ZarfComponent) { } } - if len(assets.Images) > 0 { - logrus.Info("Loading container images") - images.PullAll(assets.Images, tempPath.images) - } + if len(component.Manifests) > 0 { + spinner := message.NewProgressSpinner("Loading %d manifests", len(component.Manifests)) + defer spinner.Stop() - if assets.ManifestsPath != "" { - logrus.WithField("path", assets.ManifestsPath).Info("Loading manifests for local install") - utils.CreatePathAndCopy(assets.ManifestsPath, tempPath.manifests) + if err := utils.CreateDirectory(componentPath.manifests, 0700); err != nil { + spinner.Fatalf(err, "Unable to create the manifest path %s", componentPath.manifests) + } + + // Iterate over all manifests + for _, manifest := range component.Manifests { + for _, file := range manifest.Files { + // Copy manifests without any processing + spinner.Updatef("Copying manifest %s", file) + destination := fmt.Sprintf("%s/%s", componentPath.manifests, file) + utils.CreatePathAndCopy(file, destination) + } + for idx, kustomization := range manifest.Kustomizations { + // Generate manifests from kustomizations and place in the package + spinner.Updatef("Building kustomization for %s", kustomization) + destination := fmt.Sprintf("%s/kustomization-%s-%d.yaml", componentPath.manifests, manifest.Name, idx) + if err := kustomize.BuildKustomization(kustomization, destination); err != nil { + spinner.Fatalf(err, "unable to build the kustomization for %s", kustomization) + } + } + } + spinner.Success() } - if len(assets.Repos) > 0 { - logrus.Info("loading git repos for gitops service transfer") - // Load all specified git repos - for _, url := range assets.Repos { - // Pull all of the references if there is no `@` in the string - git.Pull(url, tempPath.repos) + // Load all specified git repos + if len(component.Repos) > 0 { + spinner := message.NewProgressSpinner("Loading %v git repos", len(component.Repos)) + defer spinner.Stop() + for _, url := range component.Repos { + // Pull all the references if there is no `@` in the string + git.Pull(url, componentPath.repos, spinner) } + spinner.Success() } } diff --git a/cli/internal/packager/deploy.go b/cli/internal/packager/deploy.go index 375ac103a7..1efa5150c0 100644 --- a/cli/internal/packager/deploy.go +++ b/cli/internal/packager/deploy.go @@ -1,259 +1,307 @@ package packager import ( - "encoding/base64" "fmt" "os" "path/filepath" - "regexp" "strconv" "strings" + "github.com/defenseunicorns/zarf/cli/types" + "github.com/defenseunicorns/zarf/cli/config" "github.com/defenseunicorns/zarf/cli/internal/git" "github.com/defenseunicorns/zarf/cli/internal/helm" "github.com/defenseunicorns/zarf/cli/internal/images" "github.com/defenseunicorns/zarf/cli/internal/k8s" - "github.com/defenseunicorns/zarf/cli/internal/pki" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/internal/template" "github.com/defenseunicorns/zarf/cli/internal/utils" "github.com/mholt/archiver/v3" "github.com/otiai10/copy" - "github.com/sirupsen/logrus" + "github.com/pterm/pterm" ) -func Deploy(packagePath string, confirm bool, componentRequest string) { - // Prevent disk pressure on smaller systems due to leaking temp files - _ = os.RemoveAll("/tmp/zarf*") +var valueTemplate template.Values +var connectStrings = make(helm.ConnectStrings) + +func Deploy() { + message.Debug("packager.Deploy()") + tempPath := createPaths() + defer tempPath.clean() - logContext := logrus.WithFields(logrus.Fields{ - "path": packagePath, - "confirm": confirm, - "components": componentRequest, - }) + spinner := message.NewProgressSpinner("Preparing zarf package %s", config.DeployOptions.PackagePath) + defer spinner.Stop() // Make sure the user gave us a package we can work with - if utils.InvalidPath(packagePath) { - logContext.Fatal("Was not able to find the package on the local system") + if utils.InvalidPath(config.DeployOptions.PackagePath) { + spinner.Fatalf(nil, "Unable to find the package on the local system, expected package at %s", config.DeployOptions.PackagePath) } // Extract the archive - logContext.Info("Extracting the package, this may take a few moments") - err := archiver.Unarchive(packagePath, tempPath.base) + spinner.Updatef("Extracting the package, this may take a few moments") + err := archiver.Unarchive(config.DeployOptions.PackagePath, tempPath.base) if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to extract the package contents") + spinner.Fatalf(err, "Unable to extract the package contents") + } + + // Load the config from the extracted archive zarf.yaml + spinner.Updatef("Loading the zarf package config") + if err := config.LoadConfig(tempPath.base + "/zarf.yaml"); err != nil { + spinner.Fatalf(err, "Invalid or unreadable zarf.yaml file in %s", tempPath.base) + } + + // Determine the proper arch now that the config file is loaded + config.SetAcrch() + + if config.IsZarfInitConfig() { + // If init config, make sure things are ready + utils.RunPreflightChecks() } + spinner.Success() + + // Confirm the overall package deployment configPath := tempPath.base + "/zarf.yaml" - confirm = confirmAction(configPath, confirm, "Deploy") + confirm := confirmAction(configPath, "Deploy") // Don't continue unless the user says so if !confirm { - cleanup(tempPath) os.Exit(0) } - // Load the config from the extracted archive zarf.yaml - if err := config.LoadConfig(tempPath.base + "/zarf.yaml"); err != nil { - logContext.Debug(err) - logContext.Fatalf("Unable to read the zarf.yaml file from %s", tempPath.base) - } - - dataInjectionList := config.GetDataInjections() - // Verify the components requested all exist components := config.GetComponents() var requestedComponents []string - if componentRequest != "" { - requestedComponents = strings.Split(componentRequest, ",") + if config.DeployOptions.Components != "" { + requestedComponents = strings.Split(config.DeployOptions.Components, ",") } componentsToDeploy := getValidComponents(components, requestedComponents) - // Deploy all of the components + // Deploy all the components for _, component := range componentsToDeploy { - componentPath := createComponentPaths(tempPath.components, component) - deployComponents(componentPath, component) + deployComponents(tempPath, component) } - if !config.IsZarfInitConfig() { + if config.IsZarfInitConfig() { + // If this is the end of an initconfig, cleanup and tell the user we're ready to roll + _ = os.Remove(".zarf-registry") + + pterm.Success.Println("Zarf deployment complete") + pterm.Println() + + _ = pterm.DefaultTable.WithHasHeader().WithData(pterm.TableData{ + {" Application", "Username", "Password", "Connect"}, + {" Logging", "zarf-admin", config.GetSecret(config.StateLogging), "zarf connect logging"}, + {" Git", config.ZarfGitPushUser, config.GetSecret(config.StateGitPush), "zarf connect git"}, + {" Registry", "zarf-push-user", config.GetSecret(config.StateRegistryPush), "zarf connect registry"}, + }).Render() + } else { + // Otherwise, look for any datainjections to run after the components + dataInjectionList := config.GetDataInjections() if len(dataInjectionList) > 0 { - logContext.Info("Loading data injections") - injectionCompletionMarker := tempPath.dataInjections + "/.zarf-sync-complete" - utils.WriteFile(injectionCompletionMarker, []byte("🦄")) - for _, data := range dataInjectionList { - sourceFile := tempPath.dataInjections + "/" + filepath.Base(data.Target.Path) - pods := k8s.WaitForPodsAndContainers(data.Target) - - for _, pod := range pods { - destination := data.Target.Path - if destination == "/"+filepath.Base(destination) { - // Handle top-level directory targets - destination = "/" - } - cpPodExecArgs := []string{"kubectl", "-n", data.Target.Namespace, "cp", sourceFile, pod + ":" + destination} - - if data.Target.Container != "" { - // Append the container args if they are specified - cpPodExecArgs = append(cpPodExecArgs, "-c", data.Target.Container) - } - - _, err = utils.ExecCommand(true, nil, config.K3sBinary, cpPodExecArgs...) - if err != nil { - logrus.Warn("Error copying data into the pod") - } else { - // Leave a marker in the target container for pods to track the sync action - cpPodExecArgs[4] = injectionCompletionMarker - cpPodExecArgs[5] = pod + ":" + data.Target.Path - _, err = utils.ExecCommand(true, nil, config.K3sBinary, cpPodExecArgs...) - if err != nil { - logrus.Warn("Error saving the zarf sync completion file") - } - } - } - // Cleanup now to reduce disk pressure - _ = os.RemoveAll(sourceFile) - } + message.Info("Loading data injections") + handleDataInjection(dataInjectionList, tempPath) } + pterm.Success.Println("Zarf deployment complete") + pterm.Println() + + if len(connectStrings) > 0 { + list := pterm.TableData{{" Connect Command", "Description"}} + // Loop over each connecStrings and convert to pterm.TableData + for name, connect := range connectStrings { + name = fmt.Sprintf(" zarf connect %s", name) + list = append(list, []string{name, connect.Description}) + } + + // Create the table output with the data + _ = pterm.DefaultTable.WithHasHeader().WithData(list).Render() + } } - cleanup(tempPath) + // All done + os.Exit(0) } -func deployComponents(tempPath componentPaths, component config.ZarfComponent) { - values := generateTemplateValues() +func deployComponents(tempPath tempPaths, component types.ZarfComponent) { + message.Debugf("packager.deployComponents(%v, %v", tempPath, component) + componentPath := createComponentPaths(tempPath.components, component) + isSeedRegistry := config.IsZarfInitConfig() && component.Name == "container-registry-seed" + hasImages := len(component.Images) > 0 + hasCharts := len(component.Charts) > 0 + hasManifests := len(component.Manifests) > 0 + hasRepos := len(component.Repos) > 0 - if component.Name != "" { - // Only log this for named components - logrus.WithField("name", component.Name).Info("Deploying Zarf component") - } else { - component.Name = "core" - } + // All components now require a name + message.HeaderInfof("📦 %s COMPONENT", strings.ToUpper(component.Name)) for _, script := range component.Scripts.Before { loopScriptUntilSuccess(script, component.Scripts.Retry) } - for index, file := range component.Files { - sourceFile := tempPath.files + "/" + strconv.Itoa(index) + if len(component.Files) > 0 { + spinner := message.NewProgressSpinner("Copying %v files", len(component.Files)) + defer spinner.Stop() - // If a shasum is specified check it again on deployment as well - if file.Shasum != "" { - utils.ValidateSha256Sum(file.Shasum, sourceFile) - } - - // Perform secret injection if the file is marked as template - if file.Template { - templateFile(sourceFile, values) - } + for index, file := range component.Files { + spinner.Updatef("Loading %s", file.Target) + sourceFile := componentPath.files + "/" + strconv.Itoa(index) - // Copy the file to the destination - err := copy.Copy(sourceFile, file.Target) - if err != nil { - logrus.Debug(err) - logrus.WithField("file", file.Target).Fatal("Unable to copy the contents of the asset") - } + // If a shasum is specified check it again on deployment as well + if file.Shasum != "" { + spinner.Updatef("Validating SHASUM for %s", file.Target) + utils.ValidateSha256Sum(file.Shasum, sourceFile) + } - for _, link := range file.Symlinks { - // Try to remove the filepath if it exists - _ = os.RemoveAll(link) - // Make sure the parent directory exists - utils.CreateFilePath(link) - // Create the symlink - err := os.Symlink(file.Target, link) + // Copy the file to the destination + spinner.Updatef("Saving %s", file.Target) + err := copy.Copy(sourceFile, file.Target) if err != nil { - logrus.Debug(err) - logrus.WithField("target", link).Fatal("Unable to create the symbolic link") + spinner.Fatalf(err, "Unable to copy the contents of %s", file.Target) } + + // Loop over all symlinks and create them + for _, link := range file.Symlinks { + spinner.Updatef("Adding symlink %s->%s", link, file.Target) + // Try to remove the filepath if it exists + _ = os.RemoveAll(link) + // Make sure the parent directory exists + _ = utils.CreateFilePath(link) + // Create the symlink + err := os.Symlink(file.Target, link) + if err != nil { + spinner.Fatalf(err, "Unable to create the symbolic link %s -> %s", link, file.Target) + } + } + + // Cleanup now to reduce disk pressure + _ = os.RemoveAll(sourceFile) } + spinner.Success() + } - // Cleanup now to reduce disk pressure - _ = os.RemoveAll(sourceFile) + if isSeedRegistry { + preSeedRegistry(tempPath) + valueTemplate = template.Generate() } - if len(component.Charts) > 0 { - logrus.Info("Loading charts for local install") - for _, chart := range component.Charts { - sourceTarball := helm.StandardName(tempPath.charts, chart) - destinationTarball := helm.StandardName(config.K3sChartPath, chart) - utils.CreatePathAndCopy(sourceTarball, destinationTarball) + if !valueTemplate.Ready() && (hasImages || hasCharts || hasManifests || hasRepos) { + // If we are touching K8s, make sure we can talk to it once per deployment + spinner := message.NewProgressSpinner("Loading the Zarf State from the Kubernetes cluster") + defer spinner.Stop() + + state := k8s.LoadZarfState() + + if state.Distro == k8s.DistroIsUnknown { + // If no distro the zarf secret did not load properly + spinner.Fatalf(nil, "Unable to load the zarf/zarf-state secret, did you remember to run zarf init first?") } - } - if len(component.Images) > 0 { - logrus.Info("Loading images for local install") - if config.IsZarfInitConfig() { - _, err := utils.ExecCommand(true, nil, config.K3sBinary, "ctr", "images", "import", tempPath.images) - if err != nil { - logrus.Fatal("Unable to import the images into containerd") - } - } else { - logrus.Info("Loading images for gitops service transfer") - // Push all images the images.tar file based on the zarf.yaml list - images.PushAll(tempPath.images, component.Images, config.GetTargetEndpoint()) - // Cleanup now to reduce disk pressure - _ = os.RemoveAll(tempPath.images) + // Continue loading state data if it is valid + config.InitState(state) + valueTemplate = template.Generate() + + if hasImages && state.Architecture != config.GetBuildData().Architecture { + // If the package has images but the architectures don't match warn the user to avoid ugly hidden errors with image push/pull + spinner.Fatalf(nil, "This package architecture is %s, but this cluster seems to be initialized with the %s architecture", + config.GetBuildData().Architecture, + state.Architecture) } + + spinner.Success() } - if component.ManifestsPath != "" { - logrus.Info("Loading manifests for local install, this may take a minute or so to reflect in k3s") + if hasImages { + images.PushToZarfRegistry(tempPath.images, component.Images, config.ZarfRegistry) + } - // Only pull in yml and yaml files - pattern := regexp.MustCompile(`(?mi)\.ya?ml$`) - manifests := utils.RecursiveFileList(tempPath.manifests, pattern) + if hasRepos { + // Push all the repos from the extracted archive + git.PushAllDirectories(componentPath.repos) + } - // Iterate through all the manifests and replace any ZARF_SECRET, ZARF_HTPASSWD, or ZARF_DOCKERAUTH values - for _, manifest := range manifests { - templateFile(manifest, values) + for _, chart := range component.Charts { + // zarf magic for the value file + for idx := range chart.ValuesFiles { + chartValueName := helm.StandardName(componentPath.values, chart) + "-" + strconv.Itoa(idx) + valueTemplate.Apply(chartValueName) } - utils.CreatePathAndCopy(tempPath.manifests, config.K3sManifestPath) + // Generate helm templates to pass to gitops engine + addedConnectStrings := helm.InstallOrUpgradeChart(helm.ChartOptions{ + BasePath: componentPath.base, + Chart: chart, + Component: component, + }) + + // Iterate over any connectStrings and add to the main map + for name, description := range addedConnectStrings { + connectStrings[name] = description + } } - if len(component.Repos) > 0 { - logrus.Info("Loading git repos for gitops service transfer") - // Push all the repos from the extracted archive - git.PushAllDirectories(tempPath.repos) + for _, manifest := range component.Manifests { + for idx := range manifest.Kustomizations { + // Move kustomizations to files now + destination := fmt.Sprintf("kustomization-%s-%d.yaml", manifest.Name, idx) + manifest.Files = append(manifest.Files, destination) + } + + // Iterate over any connectStrings and add to the main map + for name, description := range helm.GenerateChart(componentPath.manifests, manifest, component) { + connectStrings[name] = description + } } for _, script := range component.Scripts.After { loopScriptUntilSuccess(script, component.Scripts.Retry) } - if config.IsZarfInitConfig() && component.Name == "k3s" { - pki.InjectServerCert() + if isSeedRegistry { + postSeedRegistry(tempPath) } - } -type templateValues struct { - secret string - htpasswd string - dockerAuth string - endpoint string -} +// handleDataInjection performs data-copy operations into a pod +// todo: this currently requires kubectl but we should have enough k8s work to make this native now +func handleDataInjection(dataInjectionList []types.ZarfData, tempPath tempPaths) { + injectionCompletionMarker := tempPath.dataInjections + "/.zarf-sync-complete" + if err := utils.WriteFile(injectionCompletionMarker, []byte("🦄")); err != nil { + return + } + for _, data := range dataInjectionList { + sourceFile := tempPath.dataInjections + "/" + filepath.Base(data.Target.Path) + pods := k8s.WaitForPodsAndContainers(data.Target, true) + + for _, pod := range pods { + destination := data.Target.Path + if destination == "/"+filepath.Base(destination) { + // Handle top-level directory targets + destination = "/" + } + cpPodExecArgs := []string{"-n", data.Target.Namespace, "cp", sourceFile, pod + ":" + destination} -func generateTemplateValues() templateValues { - var generated templateValues - var err error + if data.Target.Container != "" { + // Append the container args if they are specified + cpPodExecArgs = append(cpPodExecArgs, "-c", data.Target.Container) + } - generated.secret = git.GetOrCreateZarfSecret() - generated.htpasswd, err = utils.GetHtpasswdString(config.ZarfGitUser, generated.secret) - if err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to define `htpasswd` string for the Zarf user") + _, err := utils.ExecCommand(true, nil, "kubectl", cpPodExecArgs...) + if err != nil { + message.Warn("Error copying data into the pod") + } else { + // Leave a marker in the target container for pods to track the sync action + cpPodExecArgs[3] = injectionCompletionMarker + cpPodExecArgs[4] = pod + ":" + data.Target.Path + _, err = utils.ExecCommand(true, nil, "kubectl", cpPodExecArgs...) + if err != nil { + message.Warn("Error saving the zarf sync completion file") + } + } + } + // Cleanup now to reduce disk pressure + _ = os.RemoveAll(sourceFile) } - generated.dockerAuth = base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", config.ZarfGitUser, generated.secret))) - generated.endpoint = config.GetTargetEndpoint() - return generated -} - -func templateFile(path string, values templateValues) { - logrus.WithField("path", path).Info("Processing file for templating") - utils.ReplaceText(path, "###ZARF_TARGET_ENDPOINT###", values.endpoint) - utils.ReplaceText(path, "###ZARF_SECRET###", values.secret) - utils.ReplaceText(path, "###ZARF_HTPASSWD###", values.htpasswd) - utils.ReplaceText(path, "###ZARF_DOCKERAUTH###", values.dockerAuth) } diff --git a/cli/internal/packager/initialize.go b/cli/internal/packager/initialize.go deleted file mode 100644 index 2ff96d2976..0000000000 --- a/cli/internal/packager/initialize.go +++ /dev/null @@ -1,48 +0,0 @@ -package packager - -import ( - "github.com/defenseunicorns/zarf/cli/config" - "github.com/defenseunicorns/zarf/cli/internal/git" - "github.com/defenseunicorns/zarf/cli/internal/utils" - "github.com/sirupsen/logrus" -) - -type InstallOptions struct { - Confirmed bool - Components string -} - -func Install(options *InstallOptions) { - utils.RunPreflightChecks() - - logrus.Info("Initializing a new zarf cluster") - - // Generate or create the zarf secret - gitSecret := git.GetOrCreateZarfSecret() - logrus.Debug("gitSecret", gitSecret) - - // Now that we have what the password will be, we should add the login entry to the system's registry config - if err := utils.Login(config.GetTargetEndpoint(), config.ZarfGitUser, gitSecret); err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to add login credentials for the gitops registry") - } - - // We really need to make sure this is still necessary.... - if utils.IsRHEL() { - // @todo: k3s docs recommend disabling this, but we should look at just tuning it appropriately - if _, err := utils.ExecCommand(true, nil, "systemctl", "disable", "firewalld", "--now"); err != nil { - logrus.Debug(err) - logrus.Warn("Unable to disable the firewall") - } - } - - // Continue running package deploy for all components like any other package - Deploy(config.PackageInitName, options.Confirmed, options.Components) - - logrus.Info("Installation complete. You can run \"/usr/local/bin/k9s\" to monitor the status of the deployment.") - logrus.WithFields(logrus.Fields{ - "Gitea Username (if installed)": config.ZarfGitUser, - "Grafana Username": "zarf-admin", - "Password (all)": gitSecret, - }).Warn("Credentials stored in ~/.git-credentials") -} diff --git a/cli/internal/packager/inspect.go b/cli/internal/packager/inspect.go index f9bddbb71d..9cdc2efa8f 100644 --- a/cli/internal/packager/inspect.go +++ b/cli/internal/packager/inspect.go @@ -1,21 +1,21 @@ package packager import ( - "fmt" "io/ioutil" "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/defenseunicorns/zarf/cli/internal/utils" "github.com/mholt/archiver/v3" - "github.com/sirupsen/logrus" ) // Inspect list the contents of a package func Inspect(packageName string) { tempPath := createPaths() + defer tempPath.clean() if utils.InvalidPath(packageName) { - logrus.WithField("archive", packageName).Fatal("The package archive seems to be missing or unreadable.") + message.Fatalf(nil, "The package archive %s seems to be missing or unreadable.", packageName) } // Extract the archive @@ -23,7 +23,7 @@ func Inspect(packageName string) { content, err := ioutil.ReadFile(tempPath.base + "/zarf.yaml") if err != nil { - logrus.Fatal(err) + message.Fatal(err, "Unable to read the config file in the package") } // Convert []byte to string and print to screen @@ -33,11 +33,8 @@ func Inspect(packageName string) { // Load the config to get the build version if err := config.LoadConfig(tempPath.base + "/zarf.yaml"); err != nil { - logrus.Fatal(err) - logrus.Fatalf("Unable to read the zarf.yaml file from %s", tempPath.base) + message.Fatalf(err, "Unable to read %s", tempPath.base) } - fmt.Printf("The package was built with Zarf CLI version %s\n", config.GetBuildData().Version) - cleanup(tempPath) - + message.Infof("The package was built with Zarf CLI version %s\n", config.GetBuildData().Version) } diff --git a/cli/internal/packager/prepare.go b/cli/internal/packager/prepare.go new file mode 100644 index 0000000000..c382e5b8c0 --- /dev/null +++ b/cli/internal/packager/prepare.go @@ -0,0 +1,278 @@ +package packager + +import ( + "fmt" + "os" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/helm" + "github.com/defenseunicorns/zarf/cli/internal/k8s" + "github.com/defenseunicorns/zarf/cli/internal/kustomize" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/internal/utils" + "github.com/defenseunicorns/zarf/cli/types" + "github.com/google/go-containerregistry/pkg/crane" + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +type ImageMap map[string]bool + +var matchedImages ImageMap +var maybeImages ImageMap + +// FindImages iterates over a zarf.yaml and attempts to parse any images +func FindImages(repoHelmChartPath string) { + + // Load the given zarf package + if err := config.LoadConfig("zarf.yaml"); err != nil { + message.Fatal(err, "Unable to read the zarf.yaml file") + } + + components := config.GetComponents() + tempPath := createPaths() + defer tempPath.clean() + + for _, component := range components { + + // matchedImages holds the collection of images, reset per-component + matchedImages = make(ImageMap) + maybeImages = make(ImageMap) + + if len(component.Charts)+len(component.Manifests)+len(component.Repos) < 1 { + // Skip if it doesn't have what we need + continue + } + + if repoHelmChartPath != "" { + // Also process git repos that have helm charts + for _, repo := range component.Repos { + matches := strings.Split(repo, "@") + if len(matches) < 2 { + message.Warnf("Cannot convert git repo %s to helm chart without a version tag", repo) + continue + } + + // Trim the first char to match how the packager expects it, this is messy,need to clean up better + repoHelmChartPath = strings.TrimPrefix(repoHelmChartPath, "/") + + // If a repo helmchartpath is specified, + component.Charts = append(component.Charts, types.ZarfChart{ + Name: repo, + Url: matches[0], + Version: matches[1], + GitPath: repoHelmChartPath, + }) + } + } + + // resources are a slice of generic structs that represent parsed K8s resources + var resources []*unstructured.Unstructured + + componentPath := createComponentPaths(tempPath.components, component) + chartNames := make(map[string]string) + + if len(component.Charts) > 0 { + _ = utils.CreateDirectory(componentPath.charts, 0700) + _ = utils.CreateDirectory(componentPath.values, 0700) + gitUrlRegex := regexp.MustCompile(`\.git$`) + + for _, chart := range component.Charts { + isGitURL := gitUrlRegex.MatchString(chart.Url) + if isGitURL { + path := helm.DownloadChartFromGit(chart, componentPath.charts) + // track the actual chart path + chartNames[chart.Name] = path + } else { + helm.DownloadPublishedChart(chart, componentPath.charts) + } + + for idx, path := range chart.ValuesFiles { + chartValueName := helm.StandardName(componentPath.values, chart) + "-" + strconv.Itoa(idx) + utils.CreatePathAndCopy(path, chartValueName) + } + + var override string + var ok bool + + if override, ok = chartNames[chart.Name]; ok { + chart.Name = "dummy" + } + + // Generate helm templates to pass to gitops engine + template, err := helm.TemplateChart(helm.ChartOptions{ + BasePath: componentPath.base, + Chart: chart, + ChartLoadOverride: override, + }) + + if err != nil { + message.Errorf(err, "Problem rendering the helm template for %s", chart.Url) + continue + } + + // Break the template into separate resources + yamls, _ := k8s.SplitYAML([]byte(template)) + resources = append(resources, yamls...) + } + } + + if len(component.Manifests) > 0 { + if err := utils.CreateDirectory(componentPath.manifests, 0700); err != nil { + message.Errorf(err, "Unable to create the manifest path %s", componentPath.manifests) + } + + for _, manifest := range component.Manifests { + for idx, kustomization := range manifest.Kustomizations { + // Generate manifests from kustomizations and place in the package + destination := fmt.Sprintf("%s/kustomization-%s-%d.yaml", componentPath.manifests, manifest.Name, idx) + if err := kustomize.BuildKustomization(kustomization, destination); err != nil { + message.Errorf(err, "unable to build the kustomization for %s", kustomization) + } else { + manifest.Files = append(manifest.Files, destination) + } + } + + // Get all manifest files + for _, file := range manifest.Files { + // Read the contents of each file + contents, err := os.ReadFile(file) + if err != nil { + message.Errorf(err, "Unable to read the file %s", file) + continue + } + + // Break the manifest into separate resources + yamls, _ := k8s.SplitYAML(contents) + resources = append(resources, yamls...) + } + } + } + + for _, resource := range resources { + if err := processUnstructured(resource); err != nil { + message.Errorf(err, "Problem processing K8s resource %s", resource.GetName()) + } + } + + if sortedImages := listImages(matchedImages, nil); len(sortedImages) > 0 { + // Log the header comment + fmt.Printf(" # %s - %s\n", config.GetMetaData().Name, component.Name) + for _, image := range sortedImages { + // Use print because we want this dumped to stdout + fmt.Println(" - " + image) + } + } + + // Handle the "maybes" + if sortedImages := listImages(maybeImages, matchedImages); len(sortedImages) > 0 { + var realImages []string + for _, image := range sortedImages { + if descriptor, err := crane.Head(image, config.ActiveCranePlatform); err != nil { + // Test if this is a real image, if not just quiet log to debug, this is normal + message.Debugf("Suspected image does not appear to be valid: %w", err) + } else { + // Otherwise, add to the list of images + message.Debugf("Imaged digest found: %s", descriptor.Digest) + realImages = append(realImages, image) + } + } + + if len(realImages) > 0 { + fmt.Println(fmt.Sprintf(" # Possible images - %s - %s", config.GetMetaData().Name, component.Name)) + for _, image := range realImages { + fmt.Println(" - " + image) + } + } + } + } +} + +func listImages(images ImageMap, compareWith ImageMap) []string { + sortedImages := sort.StringSlice{} + for image := range images { + if !compareWith[image] || compareWith == nil { + // Check compareWith, if it exists only add if not in that list + sortedImages = append(sortedImages, image) + } + } + sort.Sort(sortedImages) + return sortedImages +} + +func processUnstructured(resource *unstructured.Unstructured) error { + var imageSanityCheck = regexp.MustCompile(`(?mi)"image":"([^"]+)"`) + var imageFuzzyCheck = regexp.MustCompile(`(?mi)"([a-z0-9\-./]+:[\w][\w.\-]{0,127})"`) + var json string + + contents := resource.UnstructuredContent() + bytes, _ := resource.MarshalJSON() + json = string(bytes) + + message.Debug() + + switch resource.GetKind() { + case "Deployment": + var deployment v1.Deployment + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(contents, &deployment); err != nil { + return fmt.Errorf("could not parse deployment: %w", err) + } + processPod(deployment.Spec.Template.Spec) + + case "DaemonSet": + var daemonSet v1.DaemonSet + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(contents, &daemonSet); err != nil { + return fmt.Errorf("could not parse daemonset: %w", err) + } + processPod(daemonSet.Spec.Template.Spec) + + case "StatefulSet": + var statefulSet v1.StatefulSet + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(contents, &statefulSet); err != nil { + return fmt.Errorf("could not parse statefulset: %w", err) + } + processPod(statefulSet.Spec.Template.Spec) + + case "ReplicaSet": + var replicaSet v1.ReplicaSet + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(contents, &replicaSet); err != nil { + return fmt.Errorf("could not parse replicaset: %w", err) + } + processPod(replicaSet.Spec.Template.Spec) + + default: + // Capture any custom images + matches := imageSanityCheck.FindAllStringSubmatch(json, -1) + for _, group := range matches { + message.Debugf("Found unknown match, Kind: %s, Value: %s", resource.GetKind(), group[1]) + matchedImages[group[1]] = true + } + } + + // Capture "maybe images" too for all kinds because they might be in unexpected places.... 👀 + matches := imageFuzzyCheck.FindAllStringSubmatch(json, -1) + for _, group := range matches { + message.Debugf("Found possible fuzzy match, Kind: %s, Value: %s", resource.GetKind(), group[1]) + maybeImages[group[1]] = true + } + return nil +} + +// processPod looks for init container, ephemeral and regular container images +func processPod(pod corev1.PodSpec) { + for _, container := range pod.InitContainers { + matchedImages[container.Image] = true + } + for _, container := range pod.Containers { + matchedImages[container.Image] = true + } + for _, container := range pod.EphemeralContainers { + matchedImages[container.Image] = true + } +} diff --git a/cli/internal/packager/seed.go b/cli/internal/packager/seed.go new file mode 100644 index 0000000000..ee3451efe8 --- /dev/null +++ b/cli/internal/packager/seed.go @@ -0,0 +1,247 @@ +package packager + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/images" + "github.com/defenseunicorns/zarf/cli/internal/k8s" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/internal/message/tls" + "github.com/defenseunicorns/zarf/cli/internal/pki" + "github.com/defenseunicorns/zarf/cli/internal/utils" + "github.com/distribution/distribution/v3/configuration" + "github.com/distribution/distribution/v3/registry" + _ "github.com/distribution/distribution/v3/registry/auth/htpasswd" // used for embedded registry + _ "github.com/distribution/distribution/v3/registry/storage/driver/filesystem" // used for embedded registry +) + +var stopSeedRegistry context.CancelFunc + +func startSeedRegistry(host string, readOnly bool) { + message.Debugf("packager.startSeedRegistry(%v)", readOnly) + useTLS := host != config.IPV4Localhost + registryConfig := &configuration.Configuration{} + + if message.GetLogLevel() >= message.DebugLevel { + registryConfig.Log.Level = "debug" + } else { + registryConfig.Log.AccessLog.Disabled = true + registryConfig.Log.Formatter = "text" + registryConfig.Log.Level = "error" + } + + registryConfig.HTTP.DrainTimeout = 5 * time.Second + registryConfig.HTTP.Secret = utils.RandomString(20) + + if useTLS { + registryConfig.HTTP.TLS.Certificate = config.TLS.CertPublicPath + registryConfig.HTTP.TLS.Key = config.TLS.CertPrivatePath + } + + fileStorage := configuration.Parameters{ + "rootdirectory": ".zarf-registry", + } + + if readOnly { + if useTLS { + // Bind to any if using tls + registryConfig.HTTP.Addr = ":" + config.ZarfSeedPort + } else { + // otherwise, force localhost + registryConfig.HTTP.Addr = fmt.Sprintf("%s:%s", config.IPV4Localhost, config.ZarfSeedPort) + } + registryConfig.Storage = configuration.Storage{ + "filesystem": fileStorage, + "maintenance": configuration.Parameters{ + "readonly": map[interface{}]interface{}{ + "enabled": true, + }, + }, + } + } else { + // Read-write only listen on localhost + registryConfig.HTTP.Addr = config.ZarfLocalSeedRegistry + registryConfig.Storage = configuration.Storage{ + "filesystem": fileStorage, + } + } + + ctx, done := context.WithCancel(context.Background()) + + embeddedRegistry, err := registry.NewRegistry(ctx, registryConfig) + if err != nil { + message.Fatal(err, "Unable to start the embedded registry") + } + + //go func() { + if err := embeddedRegistry.ListenAndServe(); err != nil { + message.Fatal(err, "Unable to start the embedded registry") + } + //}() + + stopSeedRegistry = done +} + +func preSeedRegistry(tempPath tempPaths) { + message.Debugf("package.preSeedRegistry(%v)", tempPath) + + var ( + distro string + err error + inject struct { + command string + args []string + } + ) + + // Attempt to load an existing state prior to init + state := k8s.LoadZarfState() + + if state.Secret == "" || state.Distro == k8s.DistroIsUnknown { + // If the state is invalid, assume this is a new cluster + message.Debug("New cluster, no zarf state found") + + if config.DeployOptions.ApplianceMode { + // If the K3s component is being deployed, skip distro detection + distro = k8s.DistroIsK3s + state.ZarfAppliance = true + } else { + // Otherwise, trying to detect the K8s distro type + distro, err = k8s.DetectDistro() + if err != nil { + // This is a basic failure right now but likely could be polished to provide user guidance to resolve + message.Fatal(err, "Unable to connect to the k8s cluster to verify the distro") + } + } + + message.Debugf("Detected K8s distro %v", distro) + + // Defaults + state.Registry.NodePort = "31999" + state.Secret = utils.RandomString(120) + state.Distro = distro + state.Architecture = config.GetBuildData().Architecture + } + + switch state.Distro { + case k8s.DistroIsK3s: + state.StorageClass = "local-path" + state.Registry.SeedType = config.ZarfSeedTypeCLIInject + inject.command = "k3s" + inject.args = []string{"ctr", "images", "import", tempPath.seedImages} + + case k8s.DistroIsK3d: + state.StorageClass = "local-path" + clusterName := getClusterName("k3d") + state.Registry.SeedType = config.ZarfSeedTypeCLIInject + inject.command = "k3d" + inject.args = []string{"images", "import", tempPath.seedImages, "--cluster", clusterName} + + case k8s.DistroIsKind: + state.StorageClass = "standard" + // See https://github.com/kubernetes-sigs/kind/blob/v0.11.1/pkg/cluster/internal/kubeconfig/internal/kubeconfig/helpers.go#L24 + clusterName := getClusterName("kind") + state.Registry.SeedType = config.ZarfSeedTypeCLIInject + inject.command = "kind" + inject.args = []string{"load", "image-archive", tempPath.seedImages, "--name", clusterName} + + case k8s.DistroIsDockerDesktop: + state.StorageClass = "hostpath" + state.Registry.SeedType = config.ZarfSeedTypeCLIInject + inject.command = "docker" + inject.args = []string{"load", "-i", tempPath.seedImages} + + case k8s.DistroIsMicroK8s: + state.Registry.SeedType = config.ZarfSeedTypeCLIInject + inject.command = "microk8s" + inject.args = []string{"ctr", "images", "import", tempPath.seedImages} + + default: + state.Registry.SeedType = config.ZarfSeedTypeRuntimeRegistry + } + + switch state.Registry.SeedType { + case config.ZarfSeedTypeCLIInject: + var ( + output string + spinner = message.NewProgressSpinner("Injecting Zarf registry image using %s", inject.command) + ) + defer spinner.Stop() + + // If this is a seed image injection, attempt to run it and warn if there is an error + output, err = utils.ExecCommand(false, nil, inject.command, inject.args...) + message.Debug(output) + if err != nil { + spinner.Errorf(err, "Unable to inject the seed image from the %s archive", tempPath.seedImages) + spinner.Stop() + } else { + spinner.Success() + } + + // Set TLS host so that the seed template isn't broken + config.TLS.Host = config.IPV4Localhost + + case config.ZarfSeedTypeRuntimeRegistry: + // Otherwise, start embedded registry read/write (only on localhost) + startSeedRegistry(config.IPV4Localhost, false) + + // Populate the seed registry + images.PushToZarfRegistry(tempPath.seedImages, config.GetSeedImages(), config.ZarfLocalSeedRegistry) + + // Close this registry now + stopSeedRegistry() + + if config.TLS.Host == "" { + // Get user to choose/enter host info for the read-only seed registry + tls.HandleTLSOptions(config.DeployOptions.Confirm) + pki.HandlePKI() + } + + // Start the registry again read-only now + startSeedRegistry(config.TLS.Host, true) + + default: + message.Fatalf(nil, "Unknown seed registry status") + } + + // Save the state back to K8s + if err := k8s.SaveZarfState(state); err != nil { + message.Fatal(err, "Unable to save the Zarf state data back to the cluster") + } + + // Load state for the rest of the operations + config.InitState(state) + + registrySecret := config.GetSecret(config.StateRegistryPush) + // Now that we have what the password will be, we should add the login entry to the system's registry config + if err := utils.DockerLogin(config.ZarfRegistry, config.ZarfRegistryPushUser, registrySecret); err != nil { + message.Fatal(err, "Unable to add login credentials for the gitops registry") + } +} + +func postSeedRegistry(tempPath tempPaths) { + message.Debug("packager.postSeedRegistry(%v)", tempPath) + + if stopSeedRegistry != nil { + // Close the seed registry, no longer needed + stopSeedRegistry() + } + + // Push the seed images into to Zarf registry + images.PushToZarfRegistry(tempPath.seedImages, config.GetSeedImages(), config.ZarfRegistry) +} + +func getClusterName(prefix string) string { + message.Debugf("packager.getClusterName(%v)", prefix) + + if ctx, err := k8s.GetContext(); err != nil { + message.Error(err, "Unable to auto-inject the registry image into KIND") + return "" + } else { + return strings.Replace(ctx, prefix+"-", "", 1) + } +} diff --git a/cli/internal/packager/validate/validate.go b/cli/internal/packager/validate/validate.go new file mode 100644 index 0000000000..4cacae99ab --- /dev/null +++ b/cli/internal/packager/validate/validate.go @@ -0,0 +1,83 @@ +package validate + +import ( + "fmt" + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/types" +) + +// Run performs config validations and runs message.Fatal() on errors +func Run() { + components := config.GetComponents() + + for _, component := range components { + for _, chart := range component.Charts { + if err := validateChart(chart); err != nil { + message.Fatalf(err, "Invalid chart definition in the %s component: %s", component.Name, err) + } + } + for _, manifest := range component.Manifests { + if err := validateManifest(manifest); err != nil { + message.Fatalf(err, "Invalid manifest definition in the %s component: %s", component.Name, err) + } + } + } + +} + +func validateChart(chart types.ZarfChart) error { + intro := fmt.Sprintf("chart %s", chart.Name) + + // Don't allow empty names + if chart.Name == "" { + return fmt.Errorf("%s must include a name", intro) + } + + // Helm max release name + if len(chart.Name) > config.ZarfMaxChartNameLength { + return fmt.Errorf("%s exceed the maximum length of %d characters", + intro, + config.ZarfMaxChartNameLength) + } + + // Must have a namespace + if chart.Namespace == "" { + return fmt.Errorf("%s must include a namespace", intro) + } + + // Must have a url + if chart.Url == "" { + return fmt.Errorf("%s must include a url", intro) + } + + // Must have a version + if chart.Version == "" { + return fmt.Errorf("%s must include a chart version", intro) + } + + return nil +} + +func validateManifest(manifest types.ZarfManifest) error { + intro := fmt.Sprintf("chart %s", manifest.Name) + + // Don't allow empty names + if manifest.Name == "" { + return fmt.Errorf("%s must include a name", intro) + } + + // Helm max release name + if len(manifest.Name) > config.ZarfMaxChartNameLength { + return fmt.Errorf("%s exceed the maximum length of %d characters", + intro, + config.ZarfMaxChartNameLength) + } + + // Require files in manifest + if len(manifest.Files) < 1 && len(manifest.Kustomizations) < 1 { + return fmt.Errorf("%s must have at least one file or kustomization", intro) + } + + return nil +} diff --git a/cli/internal/pki/pki.go b/cli/internal/pki/pki.go index 2f19680ad0..82c1f71159 100644 --- a/cli/internal/pki/pki.go +++ b/cli/internal/pki/pki.go @@ -14,9 +14,8 @@ import ( "time" "github.com/defenseunicorns/zarf/cli/config" - "github.com/defenseunicorns/zarf/cli/internal/k8s" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/defenseunicorns/zarf/cli/internal/utils" - "github.com/sirupsen/logrus" ) // Based off of https://github.com/dmcgowan/quicktls/blob/master/main.go @@ -29,29 +28,29 @@ const org = "Zarf Cluster" const validFor = time.Hour * 24 * 375 func HandlePKI() { - pkiConfig := config.GetState().TLS + pkiConfig := config.TLS if pkiConfig.CertPublicPath == "" || pkiConfig.CertPrivatePath == "" { // No certs provided, so generate them with an ephemeral CA GeneratePKI() + pkiConfig = config.TLS } } // GeneratePKI create a CA and signed server keypair func GeneratePKI() { - state := config.GetState() directory := "zarf-pki" _ = utils.CreateDirectory(directory, 0700) caFile := filepath.Join(directory, "zarf-ca.crt") ca, caKey, err := generateCA(caFile, validFor) if err != nil { - logrus.Fatal(err) + message.Fatal(err, "Unable to generate the ephemeral CA") } hostCert := filepath.Join(directory, "zarf-server.crt") hostKey := filepath.Join(directory, "zarf-server.key") - if err := generateCert(state.TLS.Host, hostCert, hostKey, ca, caKey, validFor); err != nil { - logrus.Fatal(err) + if err := generateCert(config.TLS.Host, hostCert, hostKey, ca, caKey, validFor); err != nil { + message.Fatalf(err, "Unable to generate the cert for %s", config.TLS.Host) } publicKeyBlock := pem.Block{ @@ -61,26 +60,17 @@ func GeneratePKI() { publicKeyPem := string(pem.EncodeToMemory(&publicKeyBlock)) - state.TLS.CertPublicPath = directory + "/zarf-server.crt" - state.TLS.CertPrivatePath = directory + "/zarf-server.key" + config.TLS.CertPublicPath = directory + "/zarf-server.crt" + config.TLS.CertPrivatePath = directory + "/zarf-server.key" addCAToTrustStore(caFile) fmt.Println("Ephemeral CA below and saved to " + caFile + "\n") fmt.Println(publicKeyPem) - - if err := config.WriteState(state); err != nil { - logrus.Debug(err) - logrus.Fatal("Unable to save the zarf state file.") - } -} - -func InjectServerCert() { - k8s.ReplaceTLSSecret("kube-system", "tls-pem") } func addCAToTrustStore(caFilePath string) { - logrus.Info("Adding Ephemeral CA to the host root trust store") + message.Info("Adding Ephemeral CA to the host root trust store") rhelBinary := "update-ca-trust" debianBinary := "update-ca-certificates" @@ -89,15 +79,13 @@ func addCAToTrustStore(caFilePath string) { utils.CreatePathAndCopy(caFilePath, "/etc/pki/ca-trust/source/anchors/zarf-ca.crt") _, err := utils.ExecCommand(true, nil, rhelBinary, "extract") if err != nil { - logrus.Debug(err) - logrus.Warn("Error adding the ephemeral CA to the RHEL root trust") + message.Error(err, "Error adding the ephemeral CA to the RHEL root trust") } } else if utils.VerifyBinary(debianBinary) { utils.CreatePathAndCopy(caFilePath, "/usr/local/share/ca-certificates/extra/zarf-ca.crt") _, err := utils.ExecCommand(true, nil, debianBinary) if err != nil { - logrus.Debug(err) - logrus.Warn("Error adding the ephemeral CA to the trust store") + message.Error(err, "Error adding the ephemeral CA to the trust store") } } } @@ -110,7 +98,7 @@ func newCertificate(validFor time.Duration) *x509.Certificate { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { - logrus.Fatalf("failed to generate serial number: %s", err) + message.Fatalf(err, "failed to generate the certificate serial number") } return &x509.Certificate{ @@ -169,11 +157,14 @@ func generateCA(caFile string, validFor time.Duration) (*x509.Certificate, *rsa. } // generateCert generates a new certificate for the given host using the -// provided certificate authority. The cert and key files are stored in the +// provided certificate authority. The cert and key files are stored in // the provided files. func generateCert(host string, certFile string, keyFile string, ca *x509.Certificate, caKey *rsa.PrivateKey, validFor time.Duration) error { template := newCertificate(validFor) + template.IPAddresses = append(template.IPAddresses, net.ParseIP(config.IPV4Localhost)) + template.DNSNames = append(template.DNSNames, "docker-registry.zarf.svc.cluster.local", "git.zarf.svc.cluster.local") + // Only use SANs to keep golang happy, https://go-review.googlesource.com/c/go/+/231379 if ip := net.ParseIP(host); ip != nil { template.IPAddresses = append(template.IPAddresses, ip) diff --git a/cli/internal/template/template.go b/cli/internal/template/template.go new file mode 100644 index 0000000000..c209f66881 --- /dev/null +++ b/cli/internal/template/template.go @@ -0,0 +1,94 @@ +package template + +import ( + "fmt" + "github.com/defenseunicorns/zarf/cli/types" + + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/defenseunicorns/zarf/cli/internal/utils" +) + +type Values struct { + state types.ZarfState + registry string + seedRegistry string + secret struct { + htpasswd string + registryPush string + registryPull string + registrySecret string + gitPush string + gitPull string + logging string + } +} + +func Generate() Values { + message.Debug("template.Generate()") + var generated Values + state := config.GetState() + + generated.state = state + pushUser, errPush := utils.GetHtpasswdString(config.ZarfRegistryPushUser, config.GetSecret(config.StateRegistryPush)) + pullUser, errPull := utils.GetHtpasswdString(config.ZarfRegistryPullUser, config.GetSecret(config.StateRegistryPull)) + if errPush != nil || errPull != nil { + message.Debug(errPush, errPull) + message.Fatal(nil, "Unable to define `htpasswd` string for the Zarf user") + } + generated.secret.htpasswd = fmt.Sprintf("%s\\n%s", pushUser, pullUser) + + generated.registry = config.GetRegistry() + generated.seedRegistry = config.GetSeedRegistry() + + generated.secret.registryPush = config.GetSecret(config.StateRegistryPush) + generated.secret.registryPull = config.GetSecret(config.StateRegistryPull) + generated.secret.registrySecret = config.GetSecret(config.StateRegistrySecret) + + generated.secret.gitPush = config.GetSecret(config.StateGitPush) + generated.secret.gitPull = config.GetSecret(config.StateGitPull) + + generated.secret.logging = config.GetSecret(config.StateLogging) + + message.Debugf("Template values: %v", generated) + return generated +} + +func (values Values) Ready() bool { + return values.secret.htpasswd != "" +} + +func (values Values) GetRegistry() string { + message.Debug("template.GetRegistry()") + return values.registry +} + +func (values Values) Apply(path string) { + message.Debugf("template.Apply(%s)", path) + + if !values.Ready() { + // This should only occur if the state couldn't be pulled or on init if a template is attempted before the pre-seed stage + message.Fatalf(nil, "template.Apply() called before template.Generate()") + } + + mappings := map[string]string{ + "STORAGE_CLASS": values.state.StorageClass, + "SEED_REGISTRY": values.seedRegistry, + "REGISTRY": values.registry, + "REGISTRY_NODEPORT": values.state.Registry.NodePort, + "REGISTRY_SECRET": values.secret.registrySecret, + "REGISTRY_AUTH_PUSH": values.secret.registryPush, + "REGISTRY_AUTH_PULL": values.secret.registryPull, + "GIT_AUTH_PUSH": values.secret.gitPush, + "GIT_AUTH_PULL": values.secret.gitPull, + "LOGGING_AUTH": values.secret.logging, + "HTPASSWD": values.secret.htpasswd, + } + + message.Debug(mappings) + + for template, value := range mappings { + template = fmt.Sprintf("###ZARF_%s###", template) + utils.ReplaceText(path, template, value) + } +} diff --git a/cli/internal/utils/auth.go b/cli/internal/utils/auth.go index 26735a36e6..9eec0a37c7 100644 --- a/cli/internal/utils/auth.go +++ b/cli/internal/utils/auth.go @@ -1,18 +1,20 @@ package utils import ( + "os" + + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/types" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" - "log" - "os" ) -// Login adds the given creds to the user's Docker config, usually located at $HOME/.docker/config.yaml. It does not try +// DockerLogin adds the given creds to the user's Docker config, usually located at $HOME/.docker/config.yaml. It does not try // to connect to the given registry, it just simply adds another entry to the config file. // This function was mostly adapted from https://github.com/google/go-containerregistry/blob/5c9c442d5d68cd96787559ebf6e984c7eb084913/cmd/crane/cmd/auth.go -func Login(serverAddress string, user string, password string) error { +func DockerLogin(serverAddress string, user string, password string) error { + message.Debugf("utils.DockerLogin(%s, %s, %s)", serverAddress, user, password) cf, err := config.Load(os.Getenv("DOCKER_CONFIG")) if err != nil { return err @@ -32,6 +34,6 @@ func Login(serverAddress string, user string, password string) error { if err := cf.Save(); err != nil { return err } - log.Printf("logged in via %s", cf.Filename) + message.Debugf("logged in via %s", cf.Filename) return nil } diff --git a/cli/internal/utils/bytes.go b/cli/internal/utils/bytes.go new file mode 100644 index 0000000000..3ef9a3db5e --- /dev/null +++ b/cli/internal/utils/bytes.go @@ -0,0 +1,46 @@ +package utils + +import ( + "math" + "strconv" +) + +// forked from https://www.socketloop.com/tutorials/golang-byte-format-example + +func RoundUp(input float64, places int) (newVal float64) { + var round float64 + pow := math.Pow(10, float64(places)) + digit := pow * input + round = math.Ceil(digit) + newVal = round / pow + return +} + +func ByteFormat(inputNum float64, precision int) string { + if precision <= 0 { + precision = 1 + } + + var unit string + var returnVal float64 + + if inputNum >= 1000000000 { + returnVal = RoundUp(inputNum/1073741824, precision) + unit = " GB" // gigabyte + } else if inputNum >= 1000000 { + returnVal = RoundUp(inputNum/1048576, precision) + unit = " MB" // megabyte + } else if inputNum >= 1000 { + returnVal = RoundUp(inputNum/1024, precision) + unit = " KB" // kilobyte + } else { + returnVal = inputNum + unit = " Byte" // byte + } + + if returnVal > 1 { + unit += "s" + } + + return strconv.FormatFloat(returnVal, 'f', precision, 64) + unit +} diff --git a/cli/internal/utils/image.go b/cli/internal/utils/image.go new file mode 100644 index 0000000000..729fd41ffd --- /dev/null +++ b/cli/internal/utils/image.go @@ -0,0 +1,12 @@ +package utils + +import "regexp" + +// For further explanation see https://regex101.com/library/PiL191 and https://regex101.com/r/PiL191/1 +var hostParser = regexp.MustCompile(`(?im)^([a-z0-9\-.]+\.[a-z0-9\-]+:?[0-9]*)?/?(.+)$`) + +// SwapHost Perform base url replacment without the docker libs +func SwapHost(src string, targetHost string) string { + var substitution = targetHost + "/$2" + return hostParser.ReplaceAllString(src, substitution) +} diff --git a/cli/internal/utils/io.go b/cli/internal/utils/io.go index 719a23a7d1..d2ee72d616 100644 --- a/cli/internal/utils/io.go +++ b/cli/internal/utils/io.go @@ -2,6 +2,7 @@ package utils import ( "bytes" + "fmt" "io/ioutil" "os" "os/exec" @@ -9,22 +10,17 @@ import ( "path/filepath" "regexp" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/otiai10/copy" - "github.com/sirupsen/logrus" + "github.com/pterm/pterm" ) var TempPathPrefix = "zarf-" -func MakeTempDir() string { +func MakeTempDir() (string, error) { tmp, err := ioutil.TempDir("", TempPathPrefix) - logContext := logrus.WithField("path", tmp) - logContext.Info("Creating temp path") - - if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to create temp directory") - } - return tmp + message.Debugf("Creating temp path %s", tmp) + return tmp, err } // VerifyBinary returns true if binary is available @@ -33,12 +29,11 @@ func VerifyBinary(binary string) bool { return err == nil } -// CreateDirectory +// CreateDirectory creates a directory for the given path and file mode func CreateDirectory(path string, mode os.FileMode) error { if InvalidPath(path) { return os.MkdirAll(path, mode) } - return nil } @@ -48,12 +43,11 @@ func InvalidPath(path string) bool { return os.IsNotExist(err) } -func ListDirectories(directory string) []string { +func ListDirectories(directory string) ([]string, error) { var directories []string paths, err := os.ReadDir(directory) if err != nil { - logrus.Debug(err) - logrus.WithField("path", directory).Fatal("Unable to load the directory") + return directories, fmt.Errorf("unable to load the directory %s: %w", directory, err) } for _, entry := range paths { @@ -62,47 +56,39 @@ func ListDirectories(directory string) []string { } } - return directories + return directories, nil } -func WriteFile(path string, data []byte) { - - logContext := logrus.WithField("path", path) - +func WriteFile(path string, data []byte) error { f, err := os.Create(path) if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to create the file to write the contents") + return fmt.Errorf("unable to create the file at %s to write the contents: %w", path, err) } _, err = f.Write(data) if err != nil { _ = f.Close() - logContext.Debug(err) - logContext.Fatal("Unable to write the file contents") + return fmt.Errorf("unable to write the file at %s contents:%w", path, err) } err = f.Close() if err != nil { - logContext.Debug(err) - logContext.Fatal("Error saving file") + return fmt.Errorf("error saving file %s: %w", path, err) } + return nil } func ReplaceText(path string, old string, new string) { - logContext := logrus.WithField("path", path) input, err := ioutil.ReadFile(path) if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to load the given file") + message.Fatalf(err, "Unable to load %s", path) } output := bytes.Replace(input, []byte(old), []byte(new), -1) if err = ioutil.WriteFile(path, output, 0600); err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to update the given file") + message.Fatalf(err, "Unable to update %s", path) } } @@ -128,36 +114,25 @@ func RecursiveFileList(root string, pattern *regexp.Regexp) []string { }) if err != nil { - logrus.Debug(err) - logrus.WithField("path", root).Fatal("Unable to complete directory walking") + message.Fatalf(err, "Unable to walk the directory %s", root) } return files } -func CreateFilePath(destination string) { +func CreateFilePath(destination string) error { parentDest := path.Dir(destination) - err := CreateDirectory(parentDest, 0700) - if err != nil { - logrus.Debug(err) - logrus.WithField("path", parentDest).Fatal("Unable to create the destination path") - } + return CreateDirectory(parentDest, 0700) } func CreatePathAndCopy(source string, destination string) { - logContext := logrus.WithFields(logrus.Fields{ - "Source": source, - "Destination": destination, - }) - - logContext.Info("Copying file") - - CreateFilePath(destination) + if err := CreateFilePath(destination); err != nil { + message.Fatalf(err, "unable to copy the file %s", source) + } // Copy the asset - err := copy.Copy(source, destination) - if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to copy the contents of the asset") + if err := copy.Copy(source, destination); err != nil { + message.Fatalf(err, "unable to copy the file %s", source) } + pterm.Success.Printfln("Copying %s", source) } diff --git a/cli/internal/utils/network.go b/cli/internal/utils/network.go index f7e1ecea0b..bb34361075 100644 --- a/cli/internal/utils/network.go +++ b/cli/internal/utils/network.go @@ -1,13 +1,15 @@ package utils import ( + "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" + "path" - "github.com/sirupsen/logrus" + "github.com/defenseunicorns/zarf/cli/internal/message" + "github.com/pterm/pterm" ) func IsUrl(source string) bool { @@ -16,74 +18,79 @@ func IsUrl(source string) bool { } func Fetch(url string) io.ReadCloser { - logContext := logrus.WithFields(logrus.Fields{ - "url": url, - }) - // Get the data resp, err := http.Get(url) if err != nil { - logContext.Fatal("Unable to download the file", err) + message.Fatal(err, "Unable to download the file") } // Check server response if resp.StatusCode != http.StatusOK { - logContext.Fatalf("Bad HTTP status: %s", resp.Status) + message.Fatalf(nil, "Bad HTTP status: %s", resp.Status) } return resp.Body } -func Download(url string) []byte { - logContext := logrus.WithFields(logrus.Fields{ - "url": url, - }) - - data := Fetch(url) - - defer data.Close() - - body, err := ioutil.ReadAll(data) - if err != nil { - logContext.Fatal("Unable to download the remote file", err) - } - return body -} - func DownloadToFile(url string, target string) { - logContext := logrus.WithFields(logrus.Fields{ - "url": url, - "destination": target, - }) - - logContext.Info("Downloading file") - // Create the file destinationFile, err := os.Create(target) if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to create the destination file") + message.Fatal(err, "Unable to create the destination file") } defer destinationFile.Close() // Get the data resp, err := http.Get(url) if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to download the file", err) + message.Fatal(err, "Unable to download the file") } defer resp.Body.Close() // Check server response if resp.StatusCode != http.StatusOK { - logContext.Fatalf("Bad HTTP status: %s", resp.Status) + message.Fatalf(nil, "Bad HTTP status: %s", resp.Status) } // Writer the body to file - _, err = io.Copy(destinationFile, resp.Body) - if err != nil { - logContext.Debug(err) - logContext.Fatal("Unable to save the file", err) + text := fmt.Sprintf("Downloading %s", url) + counter := NewWriteCounter(url, int(resp.ContentLength)) + + if _, err = io.Copy(destinationFile, io.TeeReader(resp.Body, counter)); err != nil { + _, _ = counter.progress.Stop() + message.Fatalf(err, "Unable to save the file %s", target) + } + + _, _ = counter.progress.Stop() + pterm.Success.Println(text) +} + +type WriteCounter struct { + Total int + progress *pterm.ProgressbarPrinter +} + +func NewWriteCounter(url string, total int) *WriteCounter { + // keep it brief to avoid a panic on smaller windows + title := fmt.Sprintf("Downloading %s", path.Base(url)) + if total < 1 { + message.Debugf("invalid content length detected: %v", total) + } + progressBar, _ := pterm.DefaultProgressbar. + WithTotal(total). + WithShowCount(false). + WithTitle(title). + WithRemoveWhenDone(true). + Start() + return &WriteCounter{ + Total: total, + progress: progressBar, } } + +func (wc *WriteCounter) Write(p []byte) (int, error) { + n := len(p) + wc.progress.Add(n) + return n, nil +} diff --git a/cli/internal/utils/preflight.go b/cli/internal/utils/preflight.go index 4fd5010158..5808bb3a24 100644 --- a/cli/internal/utils/preflight.go +++ b/cli/internal/utils/preflight.go @@ -1,11 +1,9 @@ package utils import ( + "github.com/defenseunicorns/zarf/cli/internal/message" "os" "regexp" - "runtime" - - "github.com/sirupsen/logrus" ) func ValidHostname(hostname string) bool { @@ -21,7 +19,7 @@ func ValidHostname(hostname string) bool { } func IsValidHostName() bool { - logrus.Debug("Preflight check: validating hostname") + message.Debug("Preflight check: validating hostname") // Quick & dirty character validation instead of a complete RFC validation since the OS is already allowing it hostname, err := os.Hostname() @@ -32,39 +30,12 @@ func IsValidHostName() bool { return ValidHostname(hostname) } -func IsUserRoot() bool { - logrus.Debug("Preflight check: validating user is root") - return os.Getuid() == 0 -} - -func IsAMD64() bool { - logrus.Debug("Preflight check: validating AMD64 arch") - return runtime.GOARCH == "amd64" -} - -func IsLinux() bool { - logrus.Info("Preflight check: validating os type") - return runtime.GOOS == "linux" -} - func IsRHEL() bool { return !InvalidPath("/etc/redhat-release") } func RunPreflightChecks() { - if !IsLinux() { - logrus.Fatal("This program requires a Linux OS") - } - - if !IsAMD64() { - logrus.Fatal("This program currently only runs on AMD64 architectures") - } - - if !IsUserRoot() { - logrus.Fatal("You must run this program as root.") - } - if !IsValidHostName() { - logrus.Fatal("Please ensure this hostname is valid according to https://www.ietf.org/rfc/rfc1123.txt.") + message.Fatal(nil, "Please ensure this hostname is valid according to https://www.ietf.org/rfc/rfc1123.txt.") } } diff --git a/cli/internal/utils/random.go b/cli/internal/utils/random.go index d6e7135ecb..892c29eb65 100644 --- a/cli/internal/utils/random.go +++ b/cli/internal/utils/random.go @@ -3,7 +3,7 @@ package utils import ( "crypto/rand" - "github.com/sirupsen/logrus" + "github.com/defenseunicorns/zarf/cli/internal/message" ) // Very limited special chars for git / basic auth @@ -14,8 +14,7 @@ func RandomString(length int) string { bytes := make([]byte, length) if _, err := rand.Read(bytes); err != nil { - logrus.Debug(err) - logrus.Fatal("unable to generate a random secret") + message.Fatal(err, "unable to generate a random secret") } for i, b := range bytes { diff --git a/cli/internal/utils/shasum.go b/cli/internal/utils/shasum.go index 4ff1686df5..535f969d5f 100644 --- a/cli/internal/utils/shasum.go +++ b/cli/internal/utils/shasum.go @@ -6,17 +6,13 @@ import ( "io" "os" - "github.com/sirupsen/logrus" + "github.com/defenseunicorns/zarf/cli/internal/message" ) func ValidateSha256Sum(expectedChecksum string, path string) { actualChecksum, _ := GetSha256Sum(path) if expectedChecksum != actualChecksum { - logrus.WithFields(logrus.Fields{ - "Source": path, - "Expected": expectedChecksum, - "Actual": actualChecksum, - }).Fatal("Invalid or mismatched file checksum") + message.Fatalf("Invalid or mismatched file checksum for %s. Expected %s, computed %s", path, expectedChecksum, actualChecksum) } } @@ -27,7 +23,7 @@ func GetSha256Sum(path string) (string, error) { if IsUrl(path) { // Handle download from URL - logrus.Warn("This is a remote source. If a published checksum is available you should use that rather than calculating it directly from the remote link.") + message.Warn("This is a remote source. If a published checksum is available you should use that rather than calculating it directly from the remote link.") data = Fetch(path) } else { // Handle local file diff --git a/cli/internal/utils/yaml.go b/cli/internal/utils/yaml.go index e72f0e1ce7..a554014812 100644 --- a/cli/internal/utils/yaml.go +++ b/cli/internal/utils/yaml.go @@ -1,18 +1,18 @@ package utils -// shamelessly stolen from https://github.com/goccy/go-yaml/blob/master/cmd/ycat/ycat.go +// fork from https://github.com/goccy/go-yaml/blob/master/cmd/ycat/ycat.go import ( "fmt" "io/fs" "io/ioutil" + "github.com/defenseunicorns/zarf/cli/internal/message" "github.com/fatih/color" "github.com/goccy/go-yaml" "github.com/goccy/go-yaml/lexer" "github.com/goccy/go-yaml/printer" "github.com/mattn/go-colorable" - "github.com/sirupsen/logrus" ) const yamlEscape = "\x1b" @@ -22,7 +22,6 @@ func yamlFormat(attr color.Attribute) string { } func ColorPrintYAML(text string) { - tokens := lexer.Tokenize(text) var p printer.Printer @@ -63,15 +62,14 @@ func ColorPrintYAML(text string) { } } writer := colorable.NewColorableStdout() - _, err := writer.Write([]byte("\n\n" + p.PrintTokens(tokens) + "\n\n\n")) + _, err := writer.Write([]byte("\n" + p.PrintTokens(tokens) + "\n")) if err != nil { - logrus.Warn("Unable to print the config yaml contents") + message.Error(err, "Unable to print the config yaml contents") } } func ReadYaml(path string, destConfig interface{}) error { - logContext := logrus.WithField("path", path) - logContext.Info("Loading dynamic config") + message.Debugf("Loading zarf config %s", path) file, err := ioutil.ReadFile(path) if err != nil { diff --git a/cli/types/types.go b/cli/types/types.go new file mode 100644 index 0000000000..1ea23887d6 --- /dev/null +++ b/cli/types/types.go @@ -0,0 +1,143 @@ +package types + +// ZarfFile defines a file to deploy +type ZarfFile struct { + Source string `yaml:"source"` + Shasum string `yaml:"shasum,omitempty"` + Target string `yaml:"target"` + Executable bool `yaml:"executable,omitempty"` + Symlinks []string `yaml:"symlinks,omitempty"` +} + +// ZarfChart defines a helm chart to be deployed +type ZarfChart struct { + Name string `yaml:"name"` + Url string `yaml:"url"` + Version string `yaml:"version"` + Namespace string `yaml:"namespace"` + ValuesFiles []string `yaml:"valuesFiles,omitempty"` + GitPath string `yaml:"gitPath,omitempty"` +} + +// ZarfComponent is the primary functional grouping of assets to deploy by zarf +type ZarfComponent struct { + // Name is the unique identifier for this component + Name string `yaml:"name"` + + // Description is a message given to a user when deciding to enable this componenent or not + Description string `yaml:"description,omitempty"` + + // Default changes the default option when deploying this component + Default bool `yaml:"default,omitempty"` + + // Required makes this component mandatory for package deployment + Required bool `yaml:"required,omitempty"` + + // SecretName is the secret zarf will use for the registry, the default is "zarf-registry" + SecretName string `yaml:"secretName,omitempty"` + + // Files are files to place on disk during deploy + Files []ZarfFile `yaml:"files,omitempty"` + + // Charts are helm charts to install during package deploy + Charts []ZarfChart `yaml:"charts,omitempty"` + + // Manifests are raw manifests that get converted into zarf-generated helm charts during deploy + Manifests []ZarfManifest `yaml:"manifests,omitempty"` + + // Images are the online images needed to be included in the zarf package + Images []string `yaml:"images,omitempty"` + + // Repos are any git repos that need to be pushed into the gitea server + Repos []string `yaml:"repos,omitempty"` + + // Scripts are custom commands that run before or after package deployment + Scripts ZarfComponentScripts `yaml:"scripts,omitempty"` +} + +// ZarfManifest defines raw manifests Zarf will deploy as a helm chart +type ZarfManifest struct { + Name string `yaml:"name"` + DefaultNamespace string `yaml:"namespace,omitempty"` + Files []string `yaml:"files,omitempty"` + Kustomizations []string `yaml:"kustomizations,omitempty"` +} + +// ZarfComponentScripts are scripts that run before or after a component is deployed +type ZarfComponentScripts struct { + Retry bool `yaml:"retry,omitempty"` + Before []string `yaml:"before,omitempty"` + After []string `yaml:"after,omitempty"` +} + +// ZarfMetadata lists information about the current ZarfPackage +type ZarfMetadata struct { + Name string `yaml:"name,omitempty"` + Description string `yaml:"description,omitempty"` + Version string `yaml:"version,omitempty"` + Url string `yaml:"url,omitempty"` + Image string `yaml:"image,omitempty"` + Uncompressed bool `yaml:"uncompressed,omitempty"` + Architecture string `yaml:"architecture,omitempty"` +} + +// ZarfContainerTarget defines the destination info for a ZarfData target +type ZarfContainerTarget struct { + Namespace string `yaml:"namespace"` + Selector string `yaml:"selector"` + Container string `yaml:"container,omitempty"` + Path string `yaml:"path"` +} + +// ZarfData is a data-injection definition +type ZarfData struct { + Source string `yaml:"source"` + Target ZarfContainerTarget `yaml:"target"` +} + +// ZarfBuildData is written during the packager.Create() operation to track details of the created package +type ZarfBuildData struct { + Terminal string `yaml:"terminal"` + User string `yaml:"user"` + Architecture string `yaml:"architecture"` + Timestamp string `yaml:"timestamp"` + Version string `yaml:"string"` +} + +// ZarfPackage the top-level structure of a Zarf config file +type ZarfPackage struct { + Kind string `yaml:"kind,omitempty"` + Metadata ZarfMetadata `yaml:"metadata,omitempty"` + Build ZarfBuildData `yaml:"build,omitempty"` + Data []ZarfData `yaml:"data,omitempty"` + Components []ZarfComponent `yaml:"components,omitempty"` + Seed []string `yaml:"seed,omitempty"` +} + +// ZarfState is maintained as a secret in the Zarf namespace to track Zarf init data +type ZarfState struct { + ZarfAppliance bool `json:"zarfAppliance"` + Distro string `json:"distro"` + Architecture string `json:"architecture"` + StorageClass string `json:"storageClass"` + Secret string `json:"secret"` + Registry struct { + SeedType string `json:"seedType"` + NodePort string `json:"nodePort"` + } `json:"registry"` +} + +// TLSConfig tracks the user-defined options for TLS cert generation +type TLSConfig struct { + CertPublicPath string `yaml:"certPublicPath"` + CertPrivatePath string `yaml:"certPrivatePath"` + Host string `yaml:"host"` +} + +// ZarfDeployOptions tracks the user-defined preferences during a package deployment +type ZarfDeployOptions struct { + PackagePath string + Confirm bool + Components string + ApplianceMode bool +} diff --git a/cli/zarf.yaml b/cli/zarf.yaml index a0c00cd5f4..1a7ef42834 100644 --- a/cli/zarf.yaml +++ b/cli/zarf.yaml @@ -7,14 +7,10 @@ metadata: components: - name: baseline required: true - charts: - - name: docker-registry - url: https://helm.twun.io - version: 1.10.1 - - name: gatekeeper - url: https://repo1.dso.mil/platform-one/big-bang/apps/core/policy.git - version: 3.5.1-bb.10 + manifests: + - name: podinfo + # This will be built on the package create side and deployed as a regular manifest on package deploy + kustomizations: + - github.com/stefanprodan/podinfo//kustomize images: - - registry1.dso.mil/ironbank/kiwigrid/k8s-sidecar:1.3.0 - repos: - - https://repo1.dso.mil/platform-one/big-bang/apps/core/cluster-auditor.git@0.3.0-bb.2 + - ghcr.io/stefanprodan/podinfo:6.0.3 \ No newline at end of file diff --git a/docs/asciinema/scenarios/examples-game-logging.exp b/docs/asciinema/scenarios/examples-game-logging.exp index c01b13f334..4ca64f97fd 100755 --- a/docs/asciinema/scenarios/examples-game-logging.exp +++ b/docs/asciinema/scenarios/examples-game-logging.exp @@ -94,7 +94,7 @@ setup # prep cluster spawn bash --norc -send -h "zarf init --host=127.0.0.1 --components=management --confirm\r" +send -h "zarf init --host=127.0.0.1 --components=k3s --confirm\r" expect -timeout 120 -re {.*Grafana Username[^=]*=([^\s]*)} set grafana_user $expect_out(1,string) expect -re {.*Password \(all\)[^=]*="([^"]*)"} @@ -128,7 +128,7 @@ comment "game running?" do "kubectl get pod -l app=game"; wait_input comment "install logging component" -send -h "zarf init --host=127.0.0.1 --components=logging --confirm\r\r" +send -h "zarf init --host=127.0.0.1 --components=k3s,logging --confirm\r\r" wait_input 120 comment "PLG stack up?" diff --git a/docs/asciinema/scenarios/examples-game-scripted.exp b/docs/asciinema/scenarios/examples-game-scripted.exp index 189f31abe8..08651068ae 100755 --- a/docs/asciinema/scenarios/examples-game-scripted.exp +++ b/docs/asciinema/scenarios/examples-game-scripted.exp @@ -99,7 +99,7 @@ do "which zarf" comment "create cluster" send -h "zarf init \\\n" send -h " --host=127.0.0.1 \\\n" -send -h " --components=management \\\n" +send -h " --components=k3s \\\n" send -h " --confirm" sleep 1 ; send "\n\n" ; wait_input 120 diff --git a/docs/components.md b/docs/components.md index 519a1d5a94..b22e6e29d1 100644 --- a/docs/components.md +++ b/docs/components.md @@ -17,7 +17,7 @@ Zarf's work necessitates that some components are "always on" (a.k.a. required & | |Description| |--- |---| -|k3s |Installs a lightweight Kubernetes Cluster on the local host—[k3s](https://k3s.io/)—and configures it to start up on boot.| +|container-seed-registry|Adds a container registry so Zarf can bootstrap itself into the cluster.| |container-registry |Adds a container registry service—[docker registry](https://docs.docker.com/registry/)—into the cluster.|   @@ -31,7 +31,7 @@ These optional components are listed below along with the "magic strings" you pa |--components |Description| |--- |---| -|management |Installs tools for managing the Zarf cluster from the local host, including: [k9s](https://k9scli.io/).| +|k3s |Installs a lightweight Kubernetes Cluster on the local host—[k3s](https://k3s.io/)—and configures it to start up on boot.| |logging |Adds a log monitoring stack—[promtail / loki / graphana (a.k.a. PLG)](https://github.com/grafana/loki)—into the cluster.| |gitops-service |Adds a [GitOps](https://www.cloudbees.com/gitops/what-is-gitops)-compatible source control service—[Gitea](https://gitea.io/en-us/)—into the cluster.| diff --git a/docs/workstation.md b/docs/workstation.md index 380ac67920..3c2b789d6a 100644 --- a/docs/workstation.md +++ b/docs/workstation.md @@ -1,10 +1,9 @@ # Workstation Setup -There are several ways to use Zarf & the tooling needed depends on what plan to do with it. Here are some of the most common use cases, along with what you'll need to install on your workstation to play along. +There are several ways to use Zarf & the tooling needed depends on what you plan to do with it. Here are some of the most common use cases, along with what you'll need to install on your workstation to play along.   - ## Just gimmie Zarf! The simplest path to Zarf is to download a pre-built release and execute it on your shell (just like any other CLI tool). To do that: @@ -52,24 +51,30 @@ chmod +x ./zarf && ./zarf help # substitute ./zarf-mac-intel or ./zarf-mac-apple above, as appropriate ``` -> _**Take note**_ -> -> Commands run this way _will_ make changes to your current system / environment! -> ->This is the expected usage pattern for production but for demonstration / development & test there are better, **virtual machine**-isolated ways to run Zarf. Keep reading to find out how to get setup for those! +  -You'll know everything is installed correctly when you see the Zarf axolotl scroll through your terminal! +## I want a demo/example sandbox -  +If you're looking for an easy & low-risk way to evaluate Zarf, our recommendation is to pop into the `examples` folder. Because the demos _aren't_ intended to be long-lived and _are_ expected to clean up after themselves they are a perfect way to kick the tires. +There are lots of ways to get a sandbox environment, here's two of them: -## I want a demo/example sandbox +### Kubernetes-In-Docker (KinD) -If you're looking for an easy & low-risk way to evaluate Zarf, our recommendation is to pop into the `examples` folder. Because the demos _aren't_ intended to be long-lived and _are_ expected to clean up after themselves they've been wrapped into **virtual-machine (VM)**-isolated environments for easy setup & teardown. +1. Install [Docker](https://docs.docker.com/get-docker/). Other container engines will likely work as well but aren't actively tested by the Zarf team. -### Install +1. Install [KinD](https://github.com/kubernetes-sigs/kind). Other Kubernetes distros will work as well, but we'll be using KinD for this example since it is easy and tested frequently and thoroughly. + +1. Run + ```sh + kind create cluster + ``` + +That's it! You should now have a Kubernetes cluster running in Docker for use. Run `kind delete cluster` to clean up when you are done. -You'll need to install _these_ tools to run the examples: +### Vagrant + +You'll need to install _these_ tools to run the examples if you want to use Vagrant: 1. [Virtualbox ](https://www.virtualbox.org/wiki/Downloads) — The [hypervisor](https://www.redhat.com/en/topics/virtualization/what-is-a-hypervisor) we use to run our example VMs. @@ -91,12 +96,11 @@ You'll need to install _these_ tools to run the examples: ### Try it out -Once you've got everything installed you're ready to run some examples! We recommend giving the [Get Started - game](../examples/game/) example a try! +Once you've got everything installed you're ready to run some examples! We recommend giving the [Get Started - game](../examples/game/README.md) example a try!   - ## I need a dev machine During dev & test, Zarf gets its exercise the same way the examples do—inside a VM. Getting setup for development means that you'll need to install: @@ -105,7 +109,7 @@ During dev & test, Zarf gets its exercise the same way the examples do—ins 1. [Go](https://golang.org/doc/install) — the programming language / build tools we use to create the `zarf` (et al.) binary. - Currently recommended version is `1.16.x`. + Currently required version is `1.16.x`.   diff --git a/examples/Makefile b/examples/Makefile index 3a5386fd26..b3349167e7 100755 --- a/examples/Makefile +++ b/examples/Makefile @@ -57,21 +57,9 @@ vm-destroy: ## Cleanup plz .PHONY: package-examples package-examples: package-example-big-bang package-example-software-factory package-example-data-injection package-example-game package-example-gitops-data package-example-single-big-bang-package package-example-tiny-kafka package-example-postgres-operator ## Create zarf packages from all examples -.PHONY: vendor-big-bang-base -vendor-big-bang-base: ## Grab the bigbang base kustomization so we don't need to do funky things to let Flux grab it from a private repo - cd big-bang/template/bigbang/vendor && \ - rm -rf bigbang && \ - git init bigbang && \ - cd bigbang && \ - git remote add -f origin https://repo1.dso.mil/platform-one/big-bang/bigbang.git && \ - git config core.sparseCheckout true && \ - echo "base/" > .git/info/sparse-checkout && \ - git checkout tags/1.17.0 -b tagbranch && \ - rm -rf .git - .PHONY: package-example-big-bang -package-example-big-bang: vendor-big-bang-base ## Create the Big Bang Core example - cd big-bang && kustomize build template/bigbang/vendor/bigbang/base/flux > manifests/flux/flux-generated.yaml && $(ZARF_BIN) package create --confirm && mv zarf-package-* ../sync/ +package-example-big-bang: ## Create the Big Bang Core example + cd big-bang && $(ZARF_BIN) package create --confirm && mv zarf-package-* ../sync/ .PHONY: package-example-softare-factory package-example-software-factory: ## Create the Big Bang Software Factory example diff --git a/examples/Vagrantfile b/examples/Vagrantfile index 9c104b0d62..357e59212f 100755 --- a/examples/Vagrantfile +++ b/examples/Vagrantfile @@ -18,6 +18,9 @@ Vagrant.configure("2") do |config| config.vm.network "forwarded_port", guest: 443, host: 8443 config.vm.network "forwarded_port", guest: 9080, host: 9080 config.vm.network "forwarded_port", guest: 9443, host: 9443 + config.vm.network "forwarded_port", guest: 45001, host: 45001 + config.vm.network "forwarded_port", guest: 45002, host: 45002 + config.vm.network "forwarded_port", guest: 45003, host: 45003 config.ssh.insert_key = false config.ssh.extra_args = [ "-t", "cd /examples; sudo su" ] diff --git a/examples/big-bang/Makefile b/examples/big-bang/Makefile deleted file mode 100644 index da571b6dcc..0000000000 --- a/examples/big-bang/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -.DEFAULT_GOAL := help - -.PHONY: help -help: ## Show a list of all targets - @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ - | sed -n 's/^\(.*\): \(.*\)##\(.*\)/\1:\3/p' \ - | column -t -s ":" - -.PHONY: all -all: ## Download the latest version of Zarf, build the deploy package, and start a VM with Vagrant - @cd .. && $(MAKE) clean fetch-release package-example-big-bang vm-init - -.PHONY: all-dev -all-dev: ## Same as 'default', but build Zarf rather than downloading it - @cd .. && $(MAKE) clean build-release package-example-big-bang vm-init - -.PHONY: vm-init -vm-init: ## Bring up the VM - @cd .. && $(MAKE) vm-init - -.PHONY: vm-destroy -vm-destroy: ## Destroy the VM - @cd .. && $(MAKE) vm-destroy diff --git a/examples/big-bang/README.md b/examples/big-bang/README.md index 0d5397372a..35d17e4d72 100644 --- a/examples/big-bang/README.md +++ b/examples/big-bang/README.md @@ -1,28 +1,96 @@ -# Example: Big Bang Core All-In-One +# Example: Big Bang Core -This example deploys Big Bang Core with a gitops service. This is not normally the method that will be used in production but for a demo it works great. +This example shows a deployment of [Big Bang Core](https://repo1.dso.mil/platform-one/big-bang/bigbang) using Zarf. -Because the same cluster will be running both Traefik and Istio, Istio's VirtualServices will be available on port 9443 +![pods](img/pods.png) + +![helmreleases](img/helmreleases.png) + +## Known Issues + +- Inside the Vagrant VM the services are available on the standard port `443`. Outside the VM if you want to pull something up in your browser that traffic is being routed to port `8443` to avoid needing to be root when running the Vagrant box. +- Due to issues with Elasticsearch this example doesn't work yet in some distros. It does work in the Vagrant VM detailed below. Upcoming work to update to the latest version of Big Bang and swap the EFK stack out for the PLG stack (Promtail, Loki, Grafana) should resolve this issue +- Currently this example does the equivalent of `kustomize build | kubectl apply -f -`, which means Flux will be used to deploy everything, but it won't be watching a Git repository for changes. Upcoming work is planned to update the example so that you will be able to open up a Git repo in the private Gitea server inside the cluster, commit and push a change, and see that change get reflected in the deployment. ## Prerequisites 1. Install [Vagrant](https://www.vagrantup.com/) -2. Install `make` and `kustomize` +2. Install `make` 1. Install `sha256sum` (on Mac it's `brew install coreutils`) ## Instructions -1. `cd examples/big-bang` -1. Run one of these two commands: - - `make all` - Download the latest version of Zarf, build the deploy package, and start a VM with Vagrant - - `make all-dev` - Build Zarf locally, build the deploy package, and start a VM with Vagrant -1. Run: `./zarf init --confirm --components management,gitops-service --host 127.0.0.1` - Initialize Zarf, telling it to install the management component and gitops service and skip logging component (since BB has logging already) and tells Zarf to use `localhost` as the domain. If you want to use interactive mode instead just run `./zarf init`. -1. Wait a bit, run `k9s` to see pods come up. Don't move on until everything is running -1. Run: `./zarf package deploy zarf-package-big-bang-core-demo.tar.zst --components kubescape --confirm` - Deploy Big Bang Core. If you want interactive mode instead just run `./zarf package deploy`, it will give you a picker to choose the package. -1. Wait several minutes. Run `k9s` to watch progress -1. :warning: `kubectl delete -n istio-system envoyfilter/misdirected-request` (due to [this bug](https://repo1.dso.mil/platform-one/big-bang/bigbang/-/issues/802)) -1. Use a browser to visit the various services, available at https://*.bigbang.dev:9443 -1. When you're done, run `exit` to leave the VM then `make vm-destroy` to bring everything down +### Pull down the code and binaries + +```shell +# clone the binaries +git clone https://github.com/defenseunicorns/zarf.git + +# change to the examples folder +cd zarf/examples + +# Download the latest release of Zarf and the Init Package to the 'examples/sync' folder +make fetch-release +``` + +> NOTE: +> +> If you have any issues with `make fetch-release` you can try `make build-release` instead. It will build the files instead of downloading them. You'll need Golang installed. + +### Build the deploy package + +```shell +# Create the deploy package and move it to the 'examples/sync' folder +make package-example-big-bang +``` + +### Start the Vagrant VM + +```shell +# Start the VM. You'll be dropped into a shell in the VM as the Root user +make vm-init +``` + +> NOTE: +> +> All subsequent commands should be happening INSIDE the Vagrant VM + +### Initialize Zarf + +```shell +# Initialize Zarf +./zarf init --confirm --components k3s,gitops-service + +# (Optional) Inspect the results +./zarf tools k9s +``` + +### Deploy Big Bang + +```shell +# Deploy Big Bang +./zarf package deploy --confirm zarf-package-big-bang-core-demo.tar.zst --components kubescape + +# (Optional) Inspect the results +./zarf tools k9s +``` + +### Delete buggy EnvoyFilter + +```shell +# Delete this EnvoyFilter, it is bugged. Will be fixed when we update to a later version of Big Bang +kubectl delete -n istio-system envoyfilter/misdirected-request +``` + +### Clean Up + +```shell +# Inside the VM +exit + +# On the host +make vm-destroy +``` ## Kubescape scan @@ -36,10 +104,20 @@ kubescape scan framework nsa --use-from=/usr/local/bin/kubescape-framework-nsa.j | URL | Username | Password | Notes | | ----------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- | -| [AlertManager](https://alertmanager.bigbang.dev:9443) | n/a | n/a | Unauthenticated | -| [Grafana](https://grafana.bigbang.dev:9443) | `admin` | `prom-operator` | | -| [Kiali](https://kiali.bigbang.dev:9443) | n/a | `kubectl get secret -n kiali -o=json \| jq -r '.items[] \| select(.metadata.annotations."kubernetes.io/service-account.name"=="kiali-service-account") \| .data.token' \| base64 -d; echo` | | -| [Kibana](https://kibana.bigbang.dev:9443) | `elastic` | `kubectl get secret -n logging logging-ek-es-elastic-user -o=jsonpath='{.data.elastic}' \| base64 -d; echo` | | -| [Prometheus](https://prometheus.bigbang.dev:9443) | n/a | n/a | Unauthenticated | -| [Jaeger](https://tracing.bigbang.dev:9443) | n/a | n/a | Unauthenticated | -| [Twistlock](https://twistlock.bigbang.dev:9443) | n/a | n/a | Twistlock has you create an admin account the first time you log in | +| [AlertManager](https://alertmanager.bigbang.dev:8443) | n/a | n/a | Unauthenticated | +| [Grafana](https://grafana.bigbang.dev:8443) | `admin` | `prom-operator` | | +| [Kiali](https://kiali.bigbang.dev:8443) | n/a | `kubectl get secret -n kiali -o=json \| jq -r '.items[] \| select(.metadata.annotations."kubernetes.io/service-account.name"=="kiali-service-account") \| .data.token' \| base64 -d; echo` | | +| [Kibana](https://kibana.bigbang.dev:8443) | `elastic` | `kubectl get secret -n logging logging-ek-es-elastic-user -o=jsonpath='{.data.elastic}' \| base64 -d; echo` | | +| [Prometheus](https://prometheus.bigbang.dev:8443) | n/a | n/a | Unauthenticated | +| [Jaeger](https://tracing.bigbang.dev:8443) | n/a | n/a | Unauthenticated | +| [Twistlock](https://twistlock.bigbang.dev:8443) | n/a | n/a | Twistlock has you create an admin account the first time you log in | + +## Troubleshooting + +### Elasticsearch isn't working when I try to deploy the Big Bang package on KinD (or K3d, or any other distro other than K3s) +That's a known issue. This example is only supported right now when using the K3s cluster that Zarf is able to deploy when running `zarf init`. Updating to the latest version of Big Bang and swapping the EFK stack out for the PLG stack should fix this issue. It's on the roadmap™. +### I'm getting "Misdirected Request" when trying to get to any of the services in my browser +Run the `kubectl delete` command documented above to delete the buggy EnvoyFilter. Updating to the latest version of Big Bang will fix this issue. It's on the roadmap™. + +### My computer crashed! +Close all those hundreds of chrome tabs, shut down all non-essential programs, and try again. Big Bang is a HOG. If you have less than 32GB of RAM you're in for a rough time. diff --git a/examples/big-bang/img/helmreleases.png b/examples/big-bang/img/helmreleases.png new file mode 100644 index 0000000000..131746a84f Binary files /dev/null and b/examples/big-bang/img/helmreleases.png differ diff --git a/examples/big-bang/img/pods.png b/examples/big-bang/img/pods.png new file mode 100644 index 0000000000..6f997b2eb7 Binary files /dev/null and b/examples/big-bang/img/pods.png differ diff --git a/examples/big-bang/kustomizations/bigbang/git-secret.yaml b/examples/big-bang/kustomizations/bigbang/git-secret.yaml new file mode 100644 index 0000000000..4b2ac8c7ee --- /dev/null +++ b/examples/big-bang/kustomizations/bigbang/git-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: zarf-git-secret + namespace: bigbang +stringData: + username: "zarf-git-user" + password: "###ZARF_GIT_AUTH_PUSH###" diff --git a/examples/big-bang/template/bigbang/values.yaml b/examples/big-bang/kustomizations/bigbang/values.yaml similarity index 56% rename from examples/big-bang/template/bigbang/values.yaml rename to examples/big-bang/kustomizations/bigbang/values.yaml index f41d84b88d..7f297fa7bc 100644 --- a/examples/big-bang/template/bigbang/values.yaml +++ b/examples/big-bang/kustomizations/bigbang/values.yaml @@ -1,9 +1,9 @@ domain: bigbang.dev registryCredentials: - registry: "registry1.dso.mil" - username: "zarf-git-user" - password: "${zarf_secret}" + registry: "###ZARF_REGISTRY###" + username: "zarf-pull" + password: "###ZARF_REGISTRY_AUTH_PULL###" git: existingSecret: "zarf-git-secret" @@ -22,7 +22,7 @@ networkPolicies: istio: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__istio-controlplane.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__istio-controlplane.git ingressGateways: public-ingressgateway: type: "LoadBalancer" @@ -34,86 +34,86 @@ istio: limits: cpu: "500m" memory: "512Mi" - service: - ports: - - name: status-port - port: 15021 - protocol: TCP - targetPort: 15021 - - name: http2 - port: 9080 - protocol: TCP - targetPort: 8080 - - name: https - port: 9443 - protocol: TCP - targetPort: 8443 - - name: tls - port: 15443 - protocol: TCP - targetPort: 15443 + # service: + # ports: + # - name: status-port + # port: 15021 + # protocol: TCP + # targetPort: 15021 + # - name: http2 + # port: 9080 + # protocol: TCP + # targetPort: 8080 + # - name: https + # port: 9443 + # protocol: TCP + # targetPort: 8443 + # - name: tls + # port: 15443 + # protocol: TCP + # targetPort: 15443 gateways: public: tls: key: | -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbaLWaC86eG74Z - D5JxLJ0X4DpOTZgGeP3oY+oS5S1pE+nZq30LrC6YMQeBLSvJDWpBtvV5x9F88gMz - yhU94HgrWH26LBUQIBti+ip6IbS0sAKc6bicw6NBtR2F4BnLGw+mrUniVT8WNrRL - C1NkN5shexmTE6XAY9Ak6UpApHVmTiB8xz6hypr4JwqnqQfxDO0+AfaGSHheKo5h - xTSgUYULhyA9UaImHU+S/SekwGLRLX1KfcTpnz1+TZiQqShG9vqUB4dAge+imwAs - ZTCnI9H3tmz6jWekXQYRUraJUwjEaqqLoSQT5VQmEl518ueeRKKNB/8mi1pylWqN - UjedV4A5AgMBAAECggEBAM56xORaljBO9WAKOotNK+1rNBO6jAYTWQeY95CeolSP - y/PvobcZa6QICAL16o3DlSqQroTTmf7WllLnq4PWueA43+ETWSMaxAsqWE0laTTd - qyfV/8lvhzTv5/+z/TIZnmoCDFT2Wm9iPdudpfXbKp+ghFnYFJVwmVITRbB91InX - 38LaEvLWFnJ3/DPYursaXerwwrm50d0PCdpa/ceqBCVHlpT3Zc0lT0rYpDVtc9BG - 3gjbvKwhVUQBDfD3FGEobxhbc5eEH6JEf0PUWKnsU5F0qRKjQnfM19XKbczP+9gY - 71BDL1sALSZxxJXW865+7GeXKCtxObkcCwYbf8UrS30CgYEA+HSH4ZpuHZ8IKIbs - vFaAjsEMkRfZPao8b/g4/JCg4TuOpAdFZUTSPWmdUq3i/J8o9b+e8/bznn9HLHIT - qyreSyiRUQRtcniSL1ZUHSzzW9QefYKzPghGYHXQLIBAWt50PDaMfPQ6Sj1NaEPH - h3hq4YNYNMQP/QVmfFdiT4xVA6cCgYEA4hJgSc17hh/u84uYAKhg2zSlFG5LlYKc - Yb2aFQJhFz2QqGxMeOXyIVDFD6btGcOLtPt4RdsBuCLZZzFBDUlWL7rY9qlL+/+P - ERStyHE9gFBDa0KWfvQxHSXIuxN2mkokktiVfaTisi8SWEKRJYp+B8HCa5lSDBti - eXcGBK3hWR8CgYBJ+aBPmsR4i1ZJgsrP1M2YM4CDXt9uzdYK3JRTFtjf1vTEf+m4 - mkIiyORvrphr8ROn//La3sdwhKLzZ8/VYgEnzZ9eyPuxXpbgA0suGKkoyUJ+ykCG - Er6pj8p4xYLjy2I+X1t7BNiqLBB1H+Ezw7XHCW1k4I+GHWqDUR1TZAwX9wKBgFhy - KAm3wqPuymWuL4HSXlJkflFH9XpA5z22GBowHBwjkfzSofiKvfgayX4eKJTz1Cyy - VZO+4yVPPQ8KThEMqBN0Xn3iLkAg87ATDwpkg1M4E6hbHNX+Y1ir96R5MOWcLELn - SVUmtSpREDRHltHBJR2TyKSgD2F9NUGgN1KNVKSxAoGARyx7VceWlpdmnr+i26UH - B4h6/rL/nY7M2oWgUaj7FeygcfemtO6cV+R1Bl876Q9Dx797hZ4ddGAgxmDFsv8J - f6SSzTJBB6IGxt+1ZcxD4uFXUrOVFv00br/Re14bsXQcMwi9kEJF2idbR5E7O2qc - qbLlPssjuZS5pDnRa05bEIQ= + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDWf1Mu3PzeB7cL + KMFYUoImy+bX6aXyV1d1gDCgxvXmIkY2EMFG0yL4fqRSMExgmmTVBJmDcJixBr36 + m14t6g39FX0n0XEnB4BvxyARFnbyecB7EUQ6AGCzUTbAW16pgQC2SKuIm7qjygdS + UObgPfXqkUDshARZo1Q6OEFBZgn2R/lISg3wNlxlIp8BSCgLEE5mhAJZX9OzfVXC + zJTxXtl9IUSQwuE9ANGb0AP10xdp9gR2uyAyYXgW+d0/GTKGzJTrkcy5x39XXIuY + ddOg7dQ7lSb6VqtFXaDvnrHao1Pn/W/UG8bmGM0uDRqLdvllxp9Bj2UlHD2W9v+C + OLtxxaubAgMBAAECggEBALaCAfJHADWfVOT+2XxgP/Po3NNsL9IC9Ry6ZSX4BHS7 + RwhruzibIA9WGlUAWYx88jy6PDC1loZSGUXp+vmQRDTKmwJNWD0ASg1R3fwMJEtu + wxMz/txnQ+BvwulrFSGe7U8siB+leeoxVYd55Oh6cAsVaquULOtkaJ9dDFEsFF/j + ENySaIXqpuEG457xLL/uCfmUd7SaLhS8FbmwadvYphQK6huVpVFbBhRzLbTRFGI/ + S/kpZ9cdBIxmoZTSy1l2mveCEpgdqMbqsdLQLijYUM5ZWjW+VA+4sgmY0/oPW3Mx + M6gQUu7TQeIZFmtl05UAOICm4FjqezBmaiihjsLr3JkCgYEA78V53BvgXNvN9zzr + sn3/WDBhYxZQiGacVvirk42rT/mPr3o9tqI4pxsN3ZK3pTvBoNbRI2UrfjgAQ/+J + OVuwaheXKeTdDiSWc3suddkfAHAHECD+FZ9iSIH8t8h+sVlf7HvTe5on+JGhi2nK + 26zWv5FBjBFBgFMyXikaSQBpK20CgYEA5QPs4L1YF7HskrIeN75sbQUgNL1t+Q0H + SkOpfTZ/VbnT+92lKkdWPxmXabZLZrTYxw9/ZswrW+SgIc95kbLOiwiV46PVca/Z + fLdSBZcKqV0GWnehuh0ClhiNDJA4ZXNDucu5ZP7eWVvO/Xh3SNvhAZtFidvRuO52 + bCT7W3j5hicCgYEA4pm+BjBuRTQSnpN7qW/8j2sBzvR63b4kCOnwtX2RJv8TNWMQ + yfbcFcmyu/H6D3W/E/ORKaNmjF3+mkT5ejTWMB3lZdl+tOwNKEyFZyjwbKhzdGHJ + 38OGzkHTBhm86n0t88A+6TSSjA+OHcS4zA230spDqU1xmwaFtomf5tg1jK0CgYAs + pgRDmIaZMAYIX5OGmKh45LvvrFLJcGHQd7qOf9Z5dx4+B2tQ/9FvweSEJpcyseVl + gb774qg9ZShXDyULY8niz0yxsdpGLNuA9hiWoGjithEsCBDOwSMk8iplnaRxGvTE + P1SovQvKbhy/zAGtgbivYH9BLksH++24jck3fzFelwKBgQCbTi5jgcNO5UZc0zct + BboFbBykE1LlVOWGBB9aQ6FcKmj42DZOav4M87Yajh+2GvvgeijGvGj0xdhaWEMD + /G+OLmLlXucuzb+BAxO8jgIoQhh4gVEFOuFaXrBoQT5sMuTJDnYzxLnLXOKlMf4K + Uq/W3NP3fz/2PMW2GS19TX7EQQ== -----END PRIVATE KEY----- cert: | -----BEGIN CERTIFICATE----- - MIIFHzCCBAegAwIBAgISA9KlIFfDVyxZ1/qZXl4HMuIOMA0GCSqGSIb3DQEBCwUA + MIIFITCCBAmgAwIBAgISA1hqVGx/bDttGRuAMooDtZ7HMA0GCSqGSIb3DQEBCwUA MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD - EwJSMzAeFw0yMTA5MjcxNDU1MDdaFw0yMTEyMjYxNDU1MDZaMBgxFjAUBgNVBAMM - DSouYmlnYmFuZy5kZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb - aLWaC86eG74ZD5JxLJ0X4DpOTZgGeP3oY+oS5S1pE+nZq30LrC6YMQeBLSvJDWpB - tvV5x9F88gMzyhU94HgrWH26LBUQIBti+ip6IbS0sAKc6bicw6NBtR2F4BnLGw+m - rUniVT8WNrRLC1NkN5shexmTE6XAY9Ak6UpApHVmTiB8xz6hypr4JwqnqQfxDO0+ - AfaGSHheKo5hxTSgUYULhyA9UaImHU+S/SekwGLRLX1KfcTpnz1+TZiQqShG9vqU - B4dAge+imwAsZTCnI9H3tmz6jWekXQYRUraJUwjEaqqLoSQT5VQmEl518ueeRKKN - B/8mi1pylWqNUjedV4A5AgMBAAGjggJHMIICQzAOBgNVHQ8BAf8EBAMCBaAwHQYD + EwJSMzAeFw0yMTEyMTUxMjQzNTJaFw0yMjAzMTUxMjQzNTFaMBgxFjAUBgNVBAMM + DSouYmlnYmFuZy5kZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDW + f1Mu3PzeB7cLKMFYUoImy+bX6aXyV1d1gDCgxvXmIkY2EMFG0yL4fqRSMExgmmTV + BJmDcJixBr36m14t6g39FX0n0XEnB4BvxyARFnbyecB7EUQ6AGCzUTbAW16pgQC2 + SKuIm7qjygdSUObgPfXqkUDshARZo1Q6OEFBZgn2R/lISg3wNlxlIp8BSCgLEE5m + hAJZX9OzfVXCzJTxXtl9IUSQwuE9ANGb0AP10xdp9gR2uyAyYXgW+d0/GTKGzJTr + kcy5x39XXIuYddOg7dQ7lSb6VqtFXaDvnrHao1Pn/W/UG8bmGM0uDRqLdvllxp9B + j2UlHD2W9v+COLtxxaubAgMBAAGjggJJMIICRTAOBgNVHQ8BAf8EBAMCBaAwHQYD VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0O - BBYEFLUbMi65bMLlINPzTplLjtCHZfa0MB8GA1UdIwQYMBaAFBQusxe3WFbLrlAJ + BBYEFHsbOtfZyKFt+IqMnsHIDBHcv89oMB8GA1UdIwQYMBaAFBQusxe3WFbLrlAJ QOYfr52LFMLGMFUGCCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDovL3Iz Lm8ubGVuY3Iub3JnMCIGCCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5vcmcv MBgGA1UdEQQRMA+CDSouYmlnYmFuZy5kZXYwTAYDVR0gBEUwQzAIBgZngQwBAgEw NwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5j - cnlwdC5vcmcwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdQBElGUusO7Or8RAB9io - /ijA2uaCvtjLMbU/0zOWtbaBqAAAAXwn948JAAAEAwBGMEQCIBkkdKr6WRtmZYO8 - kuchAYDxGPaCnU9FYU3BZBpsbJvLAiButEYn4AvTFiZMILymyuuqct/eFjIR9MEE - pNotyaD+bQB2AH0+8viP/4hVaCTCwMqeUol5K8UOeAl/LmqXaJl+IvDXAAABfCf3 - kGUAAAQDAEcwRQIhAOOOX0qpI8xjqARUfU4ErGe8icHORlNHHzP/a6b3XE4ZAiBp - fMNh3oihXS1e6EM9Xs8m+9nuCi7rqLNSkCNuwisK7zANBgkqhkiG9w0BAQsFAAOC - AQEABMjkLKKxYyL4ZT6BPuOyqC4hnczDYUmZdCCysLu7psCjrZIAlSRxLIWXdWir - ogi/Vf+wdPKk38NDar0T9+rfAehuvQjQKCzIKVzr+MGauW0Wytwt63EgLIl2znvX - jWEIUwDQkqeFzPMbov8BK8hdLibBSz9nLrT0Zyw9mgRIzslemsi62+AjSNERTCTv - qyhinnBHLd3dGLOAXexwXu7ic2ZwCgnSgcli+MWC30QOh6ePJJqgw6OpwvOC9DAV - fkvGYFXlgYXnhQeLr0/4tzw3koclRWe/qgjAdAjB03yp1e53b+j9NoOfyobo1MFe - nMqEgcgAiA2VuE62Q4HE0Rs5wA== + cnlwdC5vcmcwggEFBgorBgEEAdZ5AgQCBIH2BIHzAPEAdwDfpV6raIJPH2yt7rhf + Tj5a6s2iEqRqXo47EsAgRFwqcwAAAX2+VcfaAAAEAwBIMEYCIQDPwjYC5CixLXKp + NytLx3H1gd0D3t0sCwCs8zpF++OQEwIhAOYj8nLjKWayunsZiUSBow5Tp30iNJqA + HAl00ztr1ei/AHYAKXm+8J45OSHwVnOfY6V35b5XfZxgCvj5TV0mXCVdx4QAAAF9 + vlXH1AAABAMARzBFAiAb6uSCWFwa3boOPrG7LyOc2nKMU9w/QedWI/Il6wJOmQIh + AIyhqQskxeMJZjj6v1RxPY4Y4gRDzaDql1PjnXYMDLeFMA0GCSqGSIb3DQEBCwUA + A4IBAQBsIx5S6YTk8wdnvKWos7lzsHq8+RxJ6spK5JoWRTLaOIPZKPIruNudyt4D + tbGTeiYqh1hP8uoWea8tE8yBoENAner05Wh+CyMlIoULF71lOLryRVokVYYCo/NT + HiOX4RzgX3WVeve39AU6xMCmVnRLfTHS+5kGJ+cP7rAStsMKpiiG5JM4gkNSrP/T + f++rEw1H742L5bTkbxV8K+KULhT7y1zDSgkPkG0iQgYdzWJgqrpkFM+mtcpWbKv5 + ygOJ3+D9VAyfiWjSNJ90HwswN+6uYzJsilkqBfCuew8F3sDQCJdxgRWDaSv8/iEy + 44zB6B3HDGNd7ZJkym49I12FSnnx -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw @@ -177,7 +177,12 @@ istio: Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5 -----END CERTIFICATE----- + values: + hub: "###ZARF_REGISTRY###/ironbank/opensource/istio" + cni: + image: + hub: "###ZARF_REGISTRY###/ironbank/opensource/istio" istiod: hpaSpec: maxReplicas: 1 @@ -197,8 +202,9 @@ istio: istiooperator: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__istio-operator.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__istio-operator.git values: + hub: "###ZARF_REGISTRY###/ironbank/opensource/istio" operator: resources: requests: @@ -211,8 +217,13 @@ istiooperator: jaeger: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__jaeger.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__jaeger.git values: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/jaegertracing/jaeger-operator + retention: + image: ###ZARF_REGISTRY###/ironbank/opensource/jaegertracing/jaeger-es-index-cleaner:1.24.0 + resources: requests: cpu: "100m" @@ -223,6 +234,7 @@ jaeger: jaeger: spec: allInOne: + image: ###ZARF_REGISTRY###/ironbank/opensource/jaegertracing/all-in-one:1.24.0 resources: requests: cpu: "100m" @@ -230,7 +242,14 @@ jaeger: limits: cpu: "500m" memory: "128Mi" + agent: + image: ###ZARF_REGISTRY###/ironbank/opensource/jaegertracing/jaeger-agent:1.24.0 + ingester: + image: ###ZARF_REGISTRY###/ironbank/opensource/jaegertracing/jaeger-ingester:1.24.0 + query: + image: ###ZARF_REGISTRY###/ironbank/opensource/jaegertracing/jaeger-query:1.24.0 collector: + image: ###ZARF_REGISTRY###/ironbank/opensource/jaegertracing/jaeger-collector:1.24.0 resources: requests: cpu: "100m" @@ -238,15 +257,14 @@ jaeger: limits: cpu: "500m" memory: "128Mi" - ingester: - # TODO: Remove this once the upstream bug is fixed (https://repo1.dso.mil/platform-one/big-bang/apps/core/jaeger/-/issues/15) - image: registry1.dso.mil/ironbank/opensource/jaegertracing/jaeger-ingester:1.24.0 kiali: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__kiali.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__kiali.git values: + image: + repo: ###ZARF_REGISTRY###/ironbank/opensource/kiali/kiali-operator resources: requests: cpu: "100m" @@ -257,6 +275,7 @@ kiali: cr: spec: deployment: + image_name: ###ZARF_REGISTRY###/ironbank/opensource/kiali/kiali resources: requests: cpu: "100m" @@ -264,12 +283,18 @@ kiali: limits: cpu: "500m" memory: "368Mi" + svcPatchJob: + image: + repository: ###ZARF_REGISTRY###/ironbank/big-bang/base + clusterAuditor: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__cluster-auditor.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__cluster-auditor.git values: + image: + repo: ###ZARF_REGISTRY###/ironbank/cluster-auditor/opa-collector resources: requests: cpu: "100m" @@ -281,8 +306,18 @@ clusterAuditor: gatekeeper: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__policy.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__policy.git values: + postInstall: + labelNamespace: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/kubernetes-1.21/kubectl + postUpgrade: + cleanupCRD: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/kubernetes-1.21/kubectl + image: + repository: "###ZARF_REGISTRY###/ironbank/opensource/openpolicyagent/gatekeeper" replicas: 1 controllerManager: resources: @@ -303,6 +338,8 @@ gatekeeper: violations: allowedDockerRegistries: parameters: + repos: + - ###ZARF_REGISTRY### excludedResources: # K3s kube-system stuff, better than excluding the whole namespace - "kube-system/coredns-.*" @@ -336,9 +373,11 @@ gatekeeper: logging: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__elasticsearch-kibana.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__elasticsearch-kibana.git values: elasticsearch: + image: + repository: ###ZARF_REGISTRY###/ironbank/elastic/elasticsearch/elasticsearch master: count: 1 persistence: @@ -362,6 +401,8 @@ logging: cpu: "500m" memory: "3Gi" kibana: + image: + repository: ###ZARF_REGISTRY###/ironbank/elastic/kibana/kibana count: 1 resources: requests: @@ -370,17 +411,25 @@ logging: limits: memory: "1Gi" cpu: "500m" + upgradeJob: + image: + repository: ###ZARF_REGISTRY###/ironbank/big-bang/base eckoperator: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__eck-operator.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__eck-operator.git + values: + image: + repository: ###ZARF_REGISTRY###/ironbank/elastic/eck-operator/eck-operator fluentbit: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__fluentbit.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__fluentbit.git values: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/fluent/fluent-bit securityContext: privileged: true resources: @@ -394,10 +443,12 @@ fluentbit: monitoring: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__monitoring.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__monitoring.git values: alertmanager: alertmanagerSpec: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/prometheus/alertmanager resources: requests: cpu: "100m" @@ -406,6 +457,8 @@ monitoring: cpu: "500m" memory: "256Mi" prometheusOperator: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/prometheus-operator/prometheus-operator resources: requests: cpu: "100m" @@ -413,8 +466,20 @@ monitoring: limits: cpu: "500m" memory: "512Mi" + admissionWebhooks: + patch: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/jet/kube-webhook-certgen + configmapReloadImage: + repository: ###ZARF_REGISTRY###/ironbank/opensource/jimmidyson/configmap-reload + prometheusConfigReloaderImage: + repository: ###ZARF_REGISTRY###/ironbank/opensource/prometheus-operator/prometheus-config-reloader + kubectlImage: + repository: ###ZARF_REGISTRY###/ironbank/opensource/kubernetes-1.20/kubectl-1.20 prometheus: prometheusSpec: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/prometheus/prometheus resources: requests: cpu: "100m" @@ -423,7 +488,11 @@ monitoring: cpu: "500m" memory: "2Gi" grafana: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/grafana/grafana sidecar: + image: + repository: ###ZARF_REGISTRY###/ironbank/kiwigrid/k8s-sidecar resources: requests: cpu: "50m" @@ -438,7 +507,11 @@ monitoring: limits: cpu: "500m" memory: "128Mi" + testFramework: + image: "###ZARF_REGISTRY###/ironbank/opensource/bats/bats" kube-state-metrics: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/coreos/kube-state-metrics resources: requests: cpu: "10m" @@ -447,6 +520,8 @@ monitoring: cpu: "500m" memory: "128Mi" prometheus-node-exporter: + image: + repository: ###ZARF_REGISTRY###/ironbank/opensource/prometheus/node-exporter resources: requests: cpu: "100m" @@ -458,9 +533,11 @@ monitoring: twistlock: enabled: true git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__security-tools__twistlock.git + repo: http://zarf-gitea-http.zarf.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__security-tools__twistlock.git values: console: + image: + repository: ###ZARF_REGISTRY###/ironbank/twistlock/console/console persistence: size: 5Gi resources: diff --git a/examples/big-bang/manifests/.gitignore b/examples/big-bang/manifests/.gitignore deleted file mode 100644 index f2d70df156..0000000000 --- a/examples/big-bang/manifests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*_generated.yaml -*-generated.yaml diff --git a/examples/big-bang/manifests/big-bang/manifests.yaml b/examples/big-bang/manifests/big-bang/manifests.yaml deleted file mode 100644 index 19bf3088ec..0000000000 --- a/examples/big-bang/manifests/big-bang/manifests.yaml +++ /dev/null @@ -1,101 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - owner: bigbang - name: bigbang ---- -apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: zarf-git-secret - namespace: bigbang -stringData: - username: "zarf-git-user" - password: "###ZARF_SECRET###" ---- -apiVersion: source.toolkit.fluxcd.io/v1beta1 -kind: GitRepository -metadata: - name: zarf - namespace: bigbang -spec: - ignore: | - # exclude file extensions - /**/*.md - /**/*.txt - /**/*.sh - interval: 5m - url: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__github.com__defenseunicorns__zarf.git - secretRef: - name: zarf-git-secret - ref: - branch: master -# tag: 1.21.0 ---- -apiVersion: kustomize.toolkit.fluxcd.io/v1beta1 -kind: Kustomization -metadata: - name: bigbang - namespace: bigbang -spec: - interval: 5m - path: "./examples/big-bang/template/bigbang" - prune: true - sourceRef: - kind: GitRepository - name: zarf - healthChecks: - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: bigbang - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: cluster-auditor - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: eck-operator - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: ek - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: fluent-bit - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: gatekeeper - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: istio - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: istio-operator - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: jaeger - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: kiali - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: monitoring - - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - namespace: bigbang - name: twistlock - timeout: 60m - postBuild: - substitute: - zarf_secret: "###ZARF_SECRET###" diff --git a/examples/big-bang/manifests/flux/.gitkeep b/examples/big-bang/manifests/flux/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/big-bang/manifests/flux/regcred-secret.yaml b/examples/big-bang/manifests/flux/regcred-secret.yaml deleted file mode 100644 index 5c36c6c3de..0000000000 --- a/examples/big-bang/manifests/flux/regcred-secret.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: flux-system -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } diff --git a/examples/big-bang/template/bigbang/kustomization.yaml b/examples/big-bang/template/bigbang/kustomization.yaml deleted file mode 100644 index d89c69d3f3..0000000000 --- a/examples/big-bang/template/bigbang/kustomization.yaml +++ /dev/null @@ -1,21 +0,0 @@ -bases: - - vendor/bigbang/base - -configMapGenerator: - - name: common - namespace: bigbang - behavior: merge - files: - - values.yaml - -patchesStrategicMerge: - - |- - apiVersion: source.toolkit.fluxcd.io/v1beta1 - kind: GitRepository - metadata: - name: bigbang - namespace: bigbang - spec: - url: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__bigbang.git - secretRef: - name: zarf-git-secret diff --git a/examples/big-bang/template/bigbang/vendor/bigbang/base/flux/gotk-components.yaml b/examples/big-bang/template/bigbang/vendor/bigbang/base/flux/gotk-components.yaml deleted file mode 100644 index 2a7055c0f0..0000000000 --- a/examples/big-bang/template/bigbang/vendor/bigbang/base/flux/gotk-components.yaml +++ /dev/null @@ -1,3540 +0,0 @@ ---- -# Flux version: v0.15.0 -# Components: source-controller,kustomize-controller,helm-controller,notification-controller -apiVersion: v1 -kind: Namespace -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: flux-system ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.5.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: alerts.notification.toolkit.fluxcd.io -spec: - group: notification.toolkit.fluxcd.io - names: - kind: Alert - listKind: AlertList - plural: alerts - singular: alert - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Alert is the Schema for the alerts API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: AlertSpec defines an alerting rule for events involving a - list of objects - properties: - eventSeverity: - default: info - description: Filter events based on severity, defaults to ('info'). - If set to 'info' no events will be filtered. - enum: - - info - - error - type: string - eventSources: - description: Filter events based on the involved objects. - items: - description: CrossNamespaceObjectReference contains enough information - to let you locate the typed referenced object at cluster level - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - Bucket - - GitRepository - - Kustomization - - HelmRelease - - HelmChart - - HelmRepository - - ImageRepository - - ImagePolicy - - ImageUpdateAutomation - type: string - name: - description: Name of the referent - maxLength: 53 - minLength: 1 - type: string - namespace: - description: Namespace of the referent - maxLength: 53 - minLength: 1 - type: string - required: - - name - type: object - type: array - exclusionList: - description: A list of Golang regular expressions to be used for excluding - messages. - items: - type: string - type: array - providerRef: - description: Send events using this provider. - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - summary: - description: Short description of the impact and affected cluster. - type: string - suspend: - description: This flag tells the controller to suspend subsequent - events dispatching. Defaults to false. - type: boolean - required: - - eventSources - - providerRef - type: object - status: - description: AlertStatus defines the observed state of Alert - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.5.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: buckets.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: Bucket - listKind: BucketList - plural: buckets - singular: bucket - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Bucket is the Schema for the buckets API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BucketSpec defines the desired state of an S3 compatible - bucket - properties: - bucketName: - description: The bucket name. - type: string - endpoint: - description: The bucket endpoint address. - type: string - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS S3 HTTP endpoint. - type: boolean - interval: - description: The interval at which to check for bucket updates. - type: string - provider: - default: generic - description: The S3 compatible storage provider name, default ('generic'). - enum: - - generic - - aws - type: string - region: - description: The bucket region. - type: string - secretRef: - description: The name of the secret containing authentication credentials - for the Bucket. - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 20s - description: The timeout for download operations, defaults to 20s. - type: string - required: - - bucketName - - endpoint - - interval - type: object - status: - description: BucketStatus defines the observed state of a bucket - properties: - artifact: - description: Artifact represents the output of the last successful - Bucket sync. - properties: - checksum: - description: Checksum is the SHA1 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the Bucket. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the artifact output of the - last Bucket sync. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.5.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: gitrepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: GitRepository - listKind: GitRepositoryList - plural: gitrepositories - shortNames: - - gitrepo - singular: gitrepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: GitRepository is the Schema for the gitrepositories API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: GitRepositorySpec defines the desired state of a Git repository. - properties: - gitImplementation: - default: go-git - description: Determines which git client library to use. Defaults - to go-git, valid values are ('go-git', 'libgit2'). - enum: - - go-git - - libgit2 - type: string - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - include: - description: Extra git repositories to map into the repository - items: - description: GitRepositoryInclude defines a source with a from and - to path. - properties: - fromPath: - description: The path to copy contents from, defaults to the - root directory. - type: string - repository: - description: Reference to a GitRepository to include. - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - toPath: - description: The path to copy contents to, defaults to the name - of the source ref. - type: string - required: - - repository - type: object - type: array - interval: - description: The interval at which to check for repository updates. - type: string - recurseSubmodules: - description: When enabled, after the clone is created, initializes - all submodules within, using their default settings. This option - is available only when using the 'go-git' GitImplementation. - type: boolean - ref: - description: The Git reference to checkout and monitor for changes, - defaults to master branch. - properties: - branch: - default: master - description: The Git branch to checkout, defaults to master. - type: string - commit: - description: The Git commit SHA to checkout, if specified Tag - filters will be ignored. - type: string - semver: - description: The Git tag semver expression, takes precedence over - Tag. - type: string - tag: - description: The Git tag to checkout, takes precedence over Branch. - type: string - type: object - secretRef: - description: The secret name containing the Git credentials. For HTTPS - repositories the secret must contain username and password fields. - For SSH repositories the secret must contain identity, identity.pub - and known_hosts fields. - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 20s - description: The timeout for remote Git operations like cloning, defaults - to 20s. - type: string - url: - description: The repository URL, can be a HTTP/S or SSH address. - pattern: ^(http|https|ssh):// - type: string - verify: - description: Verify OpenPGP signature for the Git commit HEAD points - to. - properties: - mode: - description: Mode describes what git object should be verified, - currently ('head'). - enum: - - head - type: string - secretRef: - description: The secret name containing the public keys of all - trusted Git authors. - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - required: - - mode - type: object - required: - - interval - - url - type: object - status: - description: GitRepositoryStatus defines the observed state of a Git repository. - properties: - artifact: - description: Artifact represents the output of the last successful - repository sync. - properties: - checksum: - description: Checksum is the SHA1 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the GitRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - includedArtifacts: - description: IncludedArtifacts represents the included artifacts from - the last successful repository sync. - items: - description: Artifact represents the output of a source synchronisation. - properties: - checksum: - description: Checksum is the SHA1 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the artifact output of the - last repository sync. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.5.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: helmcharts.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: HelmChart - listKind: HelmChartList - plural: helmcharts - shortNames: - - hc - singular: helmchart - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.chart - name: Chart - type: string - - jsonPath: .spec.version - name: Version - type: string - - jsonPath: .spec.sourceRef.kind - name: Source Kind - type: string - - jsonPath: .spec.sourceRef.name - name: Source Name - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: HelmChart is the Schema for the helmcharts API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmChartSpec defines the desired state of a Helm chart. - properties: - chart: - description: The name or path the Helm chart is available at in the - SourceRef. - type: string - interval: - description: The interval at which to check the Source for updates. - type: string - sourceRef: - description: The reference to the Source the chart is available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent, valid values are ('HelmRepository', - 'GitRepository', 'Bucket'). - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - required: - - kind - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - valuesFile: - description: Alternative values file to use as the default chart values, - expected to be a relative path in the SourceRef. Deprecated in favor - of ValuesFiles, for backwards compatibility the file defined here - is merged before the ValuesFiles items. Ignored when omitted. - type: string - valuesFiles: - description: Alternative list of values files to use as the chart - values (values.yaml is not included by default), expected to be - a relative path in the SourceRef. Values files are merged in the - order of this list with the last file overriding the first. Ignored - when omitted. - items: - type: string - type: array - version: - default: '*' - description: The chart version semver expression, ignored for charts - from GitRepository and Bucket sources. Defaults to latest when omitted. - type: string - required: - - chart - - interval - - sourceRef - type: object - status: - description: HelmChartStatus defines the observed state of the HelmChart. - properties: - artifact: - description: Artifact represents the output of the last successful - chart sync. - properties: - checksum: - description: Checksum is the SHA1 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmChart. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the last chart pulled. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.5.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: helmreleases.helm.toolkit.fluxcd.io -spec: - group: helm.toolkit.fluxcd.io - names: - kind: HelmRelease - listKind: HelmReleaseList - plural: helmreleases - shortNames: - - hr - singular: helmrelease - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v2beta1 - schema: - openAPIV3Schema: - description: HelmRelease is the Schema for the helmreleases API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmReleaseSpec defines the desired state of a Helm release. - properties: - chart: - description: Chart defines the template of the v1beta1.HelmChart that - should be created for this HelmRelease. - properties: - spec: - description: Spec holds the template for the v1beta1.HelmChartSpec - for this HelmRelease. - properties: - chart: - description: The name or path the Helm chart is available - at in the SourceRef. - type: string - interval: - description: Interval at which to check the v1beta1.Source - for updates. Defaults to 'HelmReleaseSpec.Interval'. - type: string - sourceRef: - description: The name and namespace of the v1beta1.Source - the chart is available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace of the referent. - maxLength: 63 - minLength: 1 - type: string - required: - - name - type: object - valuesFile: - description: Alternative values file to use as the default - chart values, expected to be a relative path in the SourceRef. - Deprecated in favor of ValuesFiles, for backwards compatibility - the file defined here is merged before the ValuesFiles items. - Ignored when omitted. - type: string - valuesFiles: - description: Alternative list of values files to use as the - chart values (values.yaml is not included by default), expected - to be a relative path in the SourceRef. Values files are - merged in the order of this list with the last file overriding - the first. Ignored when omitted. - items: - type: string - type: array - version: - default: '*' - description: Version semver expression, ignored for charts - from v1beta1.GitRepository and v1beta1.Bucket sources. Defaults - to latest when omitted. - type: string - required: - - chart - - sourceRef - type: object - required: - - spec - type: object - dependsOn: - description: DependsOn may contain a dependency.CrossNamespaceDependencyReference - slice with references to HelmRelease resources that must be ready - before this HelmRelease can be reconciled. - items: - description: CrossNamespaceDependencyReference holds the reference - to a dependency. - properties: - name: - description: Name holds the name reference of a dependency. - type: string - namespace: - description: Namespace holds the namespace reference of a dependency. - type: string - required: - - name - type: object - type: array - install: - description: Install holds the configuration for Helm install actions - for this HelmRelease. - properties: - crds: - description: "CRDs upgrade CRDs from the Helm Chart's crds directory - according to the CRD upgrade policy provided here. Valid values - are `Skip`, `Create` or `CreateReplace`. Default is `Create` - and if omitted CRDs are installed but not updated. \n Skip: - do neither install nor replace (update) any CRDs. \n Create: - new CRDs are created, existing CRDs are neither updated nor - deleted. \n CreateReplace: new CRDs are created, existing CRDs - are updated (replaced) but not deleted. \n By default, CRDs - are applied (installed) during Helm install action. With this - option users can opt-in to CRD replace existing CRDs on Helm - install actions, which is not (yet) natively supported by Helm. - https://helm.sh/docs/chart_best_practices/custom_resource_definitions." - enum: - - Skip - - Create - - CreateReplace - type: string - createNamespace: - description: CreateNamespace tells the Helm install action to - create the HelmReleaseSpec.TargetNamespace if it does not exist - yet. On uninstall, the namespace will not be garbage collected. - type: boolean - disableHooks: - description: DisableHooks prevents hooks from running during the - Helm install action. - type: boolean - disableOpenAPIValidation: - description: DisableOpenAPIValidation prevents the Helm install - action from validating rendered templates against the Kubernetes - OpenAPI Schema. - type: boolean - disableWait: - description: DisableWait disables the waiting for resources to - be ready after a Helm install has been performed. - type: boolean - disableWaitForJobs: - description: DisableWaitForJobs disables waiting for jobs to complete - after a Helm install has been performed. - type: boolean - remediation: - description: Remediation holds the remediation configuration for - when the Helm install action for the HelmRelease fails. The - default is to not perform any action. - properties: - ignoreTestFailures: - description: IgnoreTestFailures tells the controller to skip - remediation when the Helm tests are run after an install - action but fail. Defaults to 'Test.IgnoreFailures'. - type: boolean - remediateLastFailure: - description: RemediateLastFailure tells the controller to - remediate the last failure, when no retries remain. Defaults - to 'false'. - type: boolean - retries: - description: Retries is the number of retries that should - be attempted on failures before bailing. Remediation, using - an uninstall, is performed between each attempt. Defaults - to '0', a negative integer equals to unlimited retries. - type: integer - type: object - replace: - description: Replace tells the Helm install action to re-use the - 'ReleaseName', but only if that name is a deleted release which - remains in the history. - type: boolean - skipCRDs: - description: "SkipCRDs tells the Helm install action to not install - any CRDs. By default, CRDs are installed if not already present. - \n Deprecated use CRD policy (`crds`) attribute with value `Skip` - instead." - type: boolean - timeout: - description: Timeout is the time to wait for any individual Kubernetes - operation (like Jobs for hooks) during the performance of a - Helm install action. Defaults to 'HelmReleaseSpec.Timeout'. - type: string - type: object - interval: - description: Interval at which to reconcile the Helm release. - type: string - kubeConfig: - description: KubeConfig for reconciling the HelmRelease on a remote - cluster. When specified, KubeConfig takes precedence over ServiceAccountName. - properties: - secretRef: - description: SecretRef holds the name to a secret that contains - a 'value' key with the kubeconfig file as the value. It must - be in the same namespace as the HelmRelease. It is recommended - that the kubeconfig is self-contained, and the secret is regularly - updated if credentials such as a cloud-access-token expire. - Cloud specific `cmd-path` auth helpers will not function without - adding binaries and credentials to the Pod that is responsible - for reconciling the HelmRelease. - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - type: object - maxHistory: - description: MaxHistory is the number of revisions saved by Helm for - this HelmRelease. Use '0' for an unlimited number of revisions; - defaults to '10'. - type: integer - postRenderers: - description: PostRenderers holds an array of Helm PostRenderers, which - will be applied in order of their definition. - items: - description: PostRenderer contains a Helm PostRenderer specification. - properties: - kustomize: - description: Kustomization to apply as PostRenderer. - properties: - images: - description: Images is a list of (image name, new name, - new tag or digest) for changing image names, tags or digests. - This can also be achieved with a patch, but this operator - is simpler to specify. - items: - description: Image contains an image name, a new name, - a new tag or digest, which will replace the original - name and tag. - properties: - digest: - description: Digest is the value used to replace the - original image tag. If digest is present NewTag - value is ignored. - type: string - name: - description: Name is a tag-less image name. - type: string - newName: - description: NewName is the value used to replace - the original name. - type: string - newTag: - description: NewTag is the value used to replace the - original tag. - type: string - required: - - name - type: object - type: array - patchesJson6902: - description: JSON 6902 patches, defined as inline YAML objects. - items: - description: JSON6902Patch contains a JSON6902 patch and - the target the patch should be applied to. - properties: - patch: - description: Patch contains the JSON6902 patch document - with an array of operation objects. - items: - description: JSON6902 is a JSON6902 operation object. - https://tools.ietf.org/html/rfc6902#section-4 - properties: - from: - type: string - op: - enum: - - test - - remove - - add - - replace - - move - - copy - type: string - path: - type: string - value: - x-kubernetes-preserve-unknown-fields: true - required: - - op - - path - type: object - type: array - target: - description: Target points to the resources that the - patch document should be applied to. - properties: - annotationSelector: - description: AnnotationSelector is a string that - follows the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: Group is the API group to select - resources from. Together with Version and Kind - it is capable of unambiguously identifying and/or - selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: Kind of the API Group to select resources - from. Together with Group and Version it is - capable of unambiguously identifying and/or - selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: LabelSelector is a string that follows - the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: Version of the API Group to select - resources from. Together with Group and Kind - it is capable of unambiguously identifying and/or - selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - - target - type: object - type: array - patchesStrategicMerge: - description: Strategic merge patches, defined as inline - YAML objects. - items: - x-kubernetes-preserve-unknown-fields: true - type: array - type: object - type: object - type: array - releaseName: - description: ReleaseName used for the Helm release. Defaults to a - composition of '[TargetNamespace-]Name'. - maxLength: 53 - minLength: 1 - type: string - rollback: - description: Rollback holds the configuration for Helm rollback actions - for this HelmRelease. - properties: - cleanupOnFail: - description: CleanupOnFail allows deletion of new resources created - during the Helm rollback action when it fails. - type: boolean - disableHooks: - description: DisableHooks prevents hooks from running during the - Helm rollback action. - type: boolean - disableWait: - description: DisableWait disables the waiting for resources to - be ready after a Helm rollback has been performed. - type: boolean - disableWaitForJobs: - description: DisableWaitForJobs disables waiting for jobs to complete - after a Helm rollback has been performed. - type: boolean - force: - description: Force forces resource updates through a replacement - strategy. - type: boolean - recreate: - description: Recreate performs pod restarts for the resource if - applicable. - type: boolean - timeout: - description: Timeout is the time to wait for any individual Kubernetes - operation (like Jobs for hooks) during the performance of a - Helm rollback action. Defaults to 'HelmReleaseSpec.Timeout'. - type: string - type: object - serviceAccountName: - description: The name of the Kubernetes service account to impersonate - when reconciling this HelmRelease. - type: string - storageNamespace: - description: StorageNamespace used for the Helm storage. Defaults - to the namespace of the HelmRelease. - maxLength: 63 - minLength: 1 - type: string - suspend: - description: Suspend tells the controller to suspend reconciliation - for this HelmRelease, it does not apply to already started reconciliations. - Defaults to false. - type: boolean - targetNamespace: - description: TargetNamespace to target when performing operations - for the HelmRelease. Defaults to the namespace of the HelmRelease. - maxLength: 63 - minLength: 1 - type: string - test: - description: Test holds the configuration for Helm test actions for - this HelmRelease. - properties: - enable: - description: Enable enables Helm test actions for this HelmRelease - after an Helm install or upgrade action has been performed. - type: boolean - ignoreFailures: - description: IgnoreFailures tells the controller to skip remediation - when the Helm tests are run but fail. Can be overwritten for - tests run after install or upgrade actions in 'Install.IgnoreTestFailures' - and 'Upgrade.IgnoreTestFailures'. - type: boolean - timeout: - description: Timeout is the time to wait for any individual Kubernetes - operation during the performance of a Helm test action. Defaults - to 'HelmReleaseSpec.Timeout'. - type: string - type: object - timeout: - description: Timeout is the time to wait for any individual Kubernetes - operation (like Jobs for hooks) during the performance of a Helm - action. Defaults to '5m0s'. - type: string - uninstall: - description: Uninstall holds the configuration for Helm uninstall - actions for this HelmRelease. - properties: - disableHooks: - description: DisableHooks prevents hooks from running during the - Helm rollback action. - type: boolean - keepHistory: - description: KeepHistory tells Helm to remove all associated resources - and mark the release as deleted, but retain the release history. - type: boolean - timeout: - description: Timeout is the time to wait for any individual Kubernetes - operation (like Jobs for hooks) during the performance of a - Helm uninstall action. Defaults to 'HelmReleaseSpec.Timeout'. - type: string - type: object - upgrade: - description: Upgrade holds the configuration for Helm upgrade actions - for this HelmRelease. - properties: - cleanupOnFail: - description: CleanupOnFail allows deletion of new resources created - during the Helm upgrade action when it fails. - type: boolean - crds: - description: "CRDs upgrade CRDs from the Helm Chart's crds directory - according to the CRD upgrade policy provided here. Valid values - are `Skip`, `Create` or `CreateReplace`. Default is `Skip` and - if omitted CRDs are neither installed nor upgraded. \n Skip: - do neither install nor replace (update) any CRDs. \n Create: - new CRDs are created, existing CRDs are neither updated nor - deleted. \n CreateReplace: new CRDs are created, existing CRDs - are updated (replaced) but not deleted. \n By default, CRDs - are not applied during Helm upgrade action. With this option - users can opt-in to CRD upgrade, which is not (yet) natively - supported by Helm. https://helm.sh/docs/chart_best_practices/custom_resource_definitions." - enum: - - Skip - - Create - - CreateReplace - type: string - disableHooks: - description: DisableHooks prevents hooks from running during the - Helm upgrade action. - type: boolean - disableOpenAPIValidation: - description: DisableOpenAPIValidation prevents the Helm upgrade - action from validating rendered templates against the Kubernetes - OpenAPI Schema. - type: boolean - disableWait: - description: DisableWait disables the waiting for resources to - be ready after a Helm upgrade has been performed. - type: boolean - disableWaitForJobs: - description: DisableWaitForJobs disables waiting for jobs to complete - after a Helm upgrade has been performed. - type: boolean - force: - description: Force forces resource updates through a replacement - strategy. - type: boolean - preserveValues: - description: PreserveValues will make Helm reuse the last release's - values and merge in overrides from 'Values'. Setting this flag - makes the HelmRelease non-declarative. - type: boolean - remediation: - description: Remediation holds the remediation configuration for - when the Helm upgrade action for the HelmRelease fails. The - default is to not perform any action. - properties: - ignoreTestFailures: - description: IgnoreTestFailures tells the controller to skip - remediation when the Helm tests are run after an upgrade - action but fail. Defaults to 'Test.IgnoreFailures'. - type: boolean - remediateLastFailure: - description: RemediateLastFailure tells the controller to - remediate the last failure, when no retries remain. Defaults - to 'false' unless 'Retries' is greater than 0. - type: boolean - retries: - description: Retries is the number of retries that should - be attempted on failures before bailing. Remediation, using - 'Strategy', is performed between each attempt. Defaults - to '0', a negative integer equals to unlimited retries. - type: integer - strategy: - description: Strategy to use for failure remediation. Defaults - to 'rollback'. - enum: - - rollback - - uninstall - type: string - type: object - timeout: - description: Timeout is the time to wait for any individual Kubernetes - operation (like Jobs for hooks) during the performance of a - Helm upgrade action. Defaults to 'HelmReleaseSpec.Timeout'. - type: string - type: object - values: - description: Values holds the values for this Helm release. - x-kubernetes-preserve-unknown-fields: true - valuesFrom: - description: ValuesFrom holds references to resources containing Helm - values for this HelmRelease, and information about how they should - be merged. - items: - description: ValuesReference contains a reference to a resource - containing Helm values, and optionally the key they can be found - at. - properties: - kind: - description: Kind of the values referent, valid values are ('Secret', - 'ConfigMap'). - enum: - - Secret - - ConfigMap - type: string - name: - description: Name of the values referent. Should reside in the - same namespace as the referring resource. - maxLength: 253 - minLength: 1 - type: string - optional: - description: Optional marks this ValuesReference as optional. - When set, a not found error for the values reference is ignored, - but any ValuesKey, TargetPath or transient error will still - result in a reconciliation failure. - type: boolean - targetPath: - description: TargetPath is the YAML dot notation path the value - should be merged at. When set, the ValuesKey is expected to - be a single flat value. Defaults to 'None', which results - in the values getting merged at the root. - type: string - valuesKey: - description: ValuesKey is the data key where the values.yaml - or a specific value can be found at. Defaults to 'values.yaml'. - type: string - required: - - kind - - name - type: object - type: array - required: - - chart - - interval - type: object - status: - description: HelmReleaseStatus defines the observed state of a HelmRelease. - properties: - conditions: - description: Conditions holds the conditions for the HelmRelease. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - failures: - description: Failures is the reconciliation failure count against - the latest desired state. It is reset after a successful reconciliation. - format: int64 - type: integer - helmChart: - description: HelmChart is the namespaced name of the HelmChart resource - created by the controller for the HelmRelease. - type: string - installFailures: - description: InstallFailures is the install failure count against - the latest desired state. It is reset after a successful reconciliation. - format: int64 - type: integer - lastAppliedRevision: - description: LastAppliedRevision is the revision of the last successfully - applied source. - type: string - lastAttemptedRevision: - description: LastAttemptedRevision is the revision of the last reconciliation - attempt. - type: string - lastAttemptedValuesChecksum: - description: LastAttemptedValuesChecksum is the SHA1 checksum of the - values of the last reconciliation attempt. - type: string - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change can be detected. - type: string - lastReleaseRevision: - description: LastReleaseRevision is the revision of the last successful - Helm release. - type: integer - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - upgradeFailures: - description: UpgradeFailures is the upgrade failure count against - the latest desired state. It is reset after a successful reconciliation. - format: int64 - type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.5.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: helmrepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: HelmRepository - listKind: HelmRepositoryList - plural: helmrepositories - shortNames: - - helmrepo - singular: helmrepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: HelmRepository is the Schema for the helmrepositories API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmRepositorySpec defines the reference to a Helm repository. - properties: - interval: - description: The interval at which to check the upstream for updates. - type: string - secretRef: - description: The name of the secret containing authentication credentials - for the Helm repository. For HTTP/S basic auth the secret must contain - username and password fields. For TLS the secret must contain a - certFile and keyFile, and/or caCert fields. - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout of index downloading, defaults to 60s. - type: string - url: - description: The Helm repository URL, a valid URL contains at least - a protocol and host. - type: string - required: - - interval - - url - type: object - status: - description: HelmRepositoryStatus defines the observed state of the HelmRepository. - properties: - artifact: - description: Artifact represents the output of the last successful - repository sync. - properties: - checksum: - description: Checksum is the SHA1 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the last index fetched. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.5.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: kustomizations.kustomize.toolkit.fluxcd.io -spec: - group: kustomize.toolkit.fluxcd.io - names: - kind: Kustomization - listKind: KustomizationList - plural: kustomizations - shortNames: - - ks - singular: kustomization - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Kustomization is the Schema for the kustomizations API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: KustomizationSpec defines the desired state of a kustomization. - properties: - decryption: - description: Decrypt Kubernetes secrets before applying them on the - cluster. - properties: - provider: - description: Provider is the name of the decryption engine. - enum: - - sops - type: string - secretRef: - description: The secret name containing the private OpenPGP keys - used for decryption. - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - required: - - provider - type: object - dependsOn: - description: DependsOn may contain a dependency.CrossNamespaceDependencyReference - slice with references to Kustomization resources that must be ready - before this Kustomization can be reconciled. - items: - description: CrossNamespaceDependencyReference holds the reference - to a dependency. - properties: - name: - description: Name holds the name reference of a dependency. - type: string - namespace: - description: Namespace holds the namespace reference of a dependency. - type: string - required: - - name - type: object - type: array - force: - default: false - description: Force instructs the controller to recreate resources - when patching fails due to an immutable field change. - type: boolean - healthChecks: - description: A list of resources to be included in the health assessment. - items: - description: NamespacedObjectKindReference contains enough information - to let you locate the typed referenced object in any namespace - properties: - apiVersion: - description: API version of the referent, if not specified the - Kubernetes preferred version will be used - type: string - kind: - description: Kind of the referent - type: string - name: - description: Name of the referent - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference - type: string - required: - - kind - - name - type: object - type: array - images: - description: Images is a list of (image name, new name, new tag or - digest) for changing image names, tags or digests. This can also - be achieved with a patch, but this operator is simpler to specify. - items: - description: Image contains an image name, a new name, a new tag - or digest, which will replace the original name and tag. - properties: - digest: - description: Digest is the value used to replace the original - image tag. If digest is present NewTag value is ignored. - type: string - name: - description: Name is a tag-less image name. - type: string - newName: - description: NewName is the value used to replace the original - name. - type: string - newTag: - description: NewTag is the value used to replace the original - tag. - type: string - required: - - name - type: object - type: array - interval: - description: The interval at which to reconcile the Kustomization. - type: string - kubeConfig: - description: The KubeConfig for reconciling the Kustomization on a - remote cluster. When specified, KubeConfig takes precedence over - ServiceAccountName. - properties: - secretRef: - description: SecretRef holds the name to a secret that contains - a 'value' key with the kubeconfig file as the value. It must - be in the same namespace as the Kustomization. It is recommended - that the kubeconfig is self-contained, and the secret is regularly - updated if credentials such as a cloud-access-token expire. - Cloud specific `cmd-path` auth helpers will not function without - adding binaries and credentials to the Pod that is responsible - for reconciling the Kustomization. - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - type: object - patches: - description: Patches (also called overlays), defined as inline YAML - objects. - items: - description: Patch contains either a StrategicMerge or a JSON6902 - patch, either a file or inline, and the target the patch should - be applied to. - properties: - patch: - description: Patch contains the JSON6902 patch document with - an array of operation objects. - type: string - target: - description: Target points to the resources that the patch document - should be applied to. - properties: - annotationSelector: - description: AnnotationSelector is a string that follows - the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: Group is the API group to select resources - from. Together with Version and Kind it is capable of - unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: LabelSelector is a string that follows the - label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: Version of the API Group to select resources - from. Together with Group and Kind it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - type: object - type: array - patchesJson6902: - description: JSON 6902 patches, defined as inline YAML objects. - items: - description: JSON6902Patch contains a JSON6902 patch and the target - the patch should be applied to. - properties: - patch: - description: Patch contains the JSON6902 patch document with - an array of operation objects. - items: - description: JSON6902 is a JSON6902 operation object. https://tools.ietf.org/html/rfc6902#section-4 - properties: - from: - type: string - op: - enum: - - test - - remove - - add - - replace - - move - - copy - type: string - path: - type: string - value: - x-kubernetes-preserve-unknown-fields: true - required: - - op - - path - type: object - type: array - target: - description: Target points to the resources that the patch document - should be applied to. - properties: - annotationSelector: - description: AnnotationSelector is a string that follows - the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: Group is the API group to select resources - from. Together with Version and Kind it is capable of - unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: LabelSelector is a string that follows the - label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: Version of the API Group to select resources - from. Together with Group and Kind it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - - target - type: object - type: array - patchesStrategicMerge: - description: Strategic merge patches, defined as inline YAML objects. - items: - x-kubernetes-preserve-unknown-fields: true - type: array - path: - description: Path to the directory containing the kustomization.yaml - file, or the set of plain YAMLs a kustomization.yaml should be generated - for. Defaults to 'None', which translates to the root path of the - SourceRef. - type: string - postBuild: - description: PostBuild describes which actions to perform on the YAML - manifest generated by building the kustomize overlay. - properties: - substitute: - additionalProperties: - type: string - description: Substitute holds a map of key/value pairs. The variables - defined in your YAML manifests that match any of the keys defined - in the map will be substituted with the set value. Includes - support for bash string replacement functions e.g. ${var:=default}, - ${var:position} and ${var/substring/replacement}. - type: object - substituteFrom: - description: SubstituteFrom holds references to ConfigMaps and - Secrets containing the variables and their values to be substituted - in the YAML manifests. The ConfigMap and the Secret data keys - represent the var names and they must match the vars declared - in the manifests for the substitution to happen. - items: - description: SubstituteReference contains a reference to a resource - containing the variables name and value. - properties: - kind: - description: Kind of the values referent, valid values are - ('Secret', 'ConfigMap'). - enum: - - Secret - - ConfigMap - type: string - name: - description: Name of the values referent. Should reside - in the same namespace as the referring resource. - maxLength: 253 - minLength: 1 - type: string - required: - - kind - - name - type: object - type: array - type: object - prune: - description: Prune enables garbage collection. - type: boolean - retryInterval: - description: The interval at which to retry a previously failed reconciliation. - When not specified, the controller uses the KustomizationSpec.Interval - value to retry failures. - type: string - serviceAccountName: - description: The name of the Kubernetes service account to impersonate - when reconciling this Kustomization. - type: string - sourceRef: - description: Reference of the source where the kustomization file - is. - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - GitRepository - - Bucket - type: string - name: - description: Name of the referent - type: string - namespace: - description: Namespace of the referent, defaults to the Kustomization - namespace - type: string - required: - - kind - - name - type: object - suspend: - description: This flag tells the controller to suspend subsequent - kustomize executions, it does not apply to already started executions. - Defaults to false. - type: boolean - targetNamespace: - description: TargetNamespace sets or overrides the namespace in the - kustomization.yaml file. - maxLength: 63 - minLength: 1 - type: string - timeout: - description: Timeout for validation, apply and health checking operations. - Defaults to 'Interval' duration. - type: string - validation: - description: Validate the Kubernetes objects before applying them - on the cluster. The validation strategy can be 'client' (local dry-run), - 'server' (APIServer dry-run) or 'none'. When 'Force' is 'true', - validation will fallback to 'client' if set to 'server' because - server-side validation is not supported in this scenario. - enum: - - none - - client - - server - type: string - required: - - interval - - prune - - sourceRef - type: object - status: - description: KustomizationStatus defines the observed state of a kustomization. - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastAppliedRevision: - description: The last successfully applied revision. The revision - format for Git sources is /. - type: string - lastAttemptedRevision: - description: LastAttemptedRevision is the revision of the last reconciliation - attempt. - type: string - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - snapshot: - description: The last successfully applied revision metadata. - properties: - checksum: - description: The manifests sha1 checksum. - type: string - entries: - description: A list of Kubernetes kinds grouped by namespace. - items: - description: Snapshot holds the metadata of namespaced Kubernetes - objects - properties: - kinds: - additionalProperties: - type: string - description: The list of Kubernetes kinds. - type: object - namespace: - description: The namespace of this entry. - type: string - required: - - kinds - type: object - type: array - required: - - checksum - - entries - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.5.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: providers.notification.toolkit.fluxcd.io -spec: - group: notification.toolkit.fluxcd.io - names: - kind: Provider - listKind: ProviderList - plural: providers - singular: provider - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Provider is the Schema for the providers API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ProviderSpec defines the desired state of Provider - properties: - address: - description: HTTP/S webhook address of this provider - pattern: ^(http|https):// - type: string - certSecretRef: - description: CertSecretRef can be given the name of a secret containing - a PEM-encoded CA certificate (`caFile`) - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - channel: - description: Alert channel for this provider - type: string - proxy: - description: HTTP/S address of the proxy - pattern: ^(http|https):// - type: string - secretRef: - description: Secret reference containing the provider webhook URL - using "address" as data key - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - type: - description: Type of provider - enum: - - slack - - discord - - msteams - - rocket - - generic - - github - - gitlab - - bitbucket - - azuredevops - - googlechat - - webex - - sentry - - azureeventhub - type: string - username: - description: Bot username for this provider - type: string - required: - - type - type: object - status: - description: ProviderStatus defines the observed state of Provider - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.5.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: receivers.notification.toolkit.fluxcd.io -spec: - group: notification.toolkit.fluxcd.io - names: - kind: Receiver - listKind: ReceiverList - plural: receivers - singular: receiver - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Receiver is the Schema for the receivers API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ReceiverSpec defines the desired state of Receiver - properties: - events: - description: A list of events to handle, e.g. 'push' for GitHub or - 'Push Hook' for GitLab. - items: - type: string - type: array - resources: - description: A list of resources to be notified about changes. - items: - description: CrossNamespaceObjectReference contains enough information - to let you locate the typed referenced object at cluster level - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - Bucket - - GitRepository - - Kustomization - - HelmRelease - - HelmChart - - HelmRepository - - ImageRepository - - ImagePolicy - - ImageUpdateAutomation - type: string - name: - description: Name of the referent - maxLength: 53 - minLength: 1 - type: string - namespace: - description: Namespace of the referent - maxLength: 53 - minLength: 1 - type: string - required: - - name - type: object - type: array - secretRef: - description: Secret reference containing the token used to validate - the payload authenticity - properties: - name: - description: Name of the referent - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend subsequent - events handling. Defaults to false. - type: boolean - type: - description: Type of webhook sender, used to determine the validation - procedure and payload deserialization. - enum: - - generic - - generic-hmac - - github - - gitlab - - bitbucket - - harbor - - dockerhub - - quay - - gcr - - nexus - - acr - type: string - required: - - resources - - type - type: object - status: - description: ReceiverStatus defines the observed state of Receiver - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: Generated webhook URL in the format of '/hook/sha256sum(token+name+namespace)'. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: helm-controller - namespace: flux-system ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: kustomize-controller - namespace: flux-system ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: notification-controller - namespace: flux-system ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: source-controller - namespace: flux-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: crd-controller-flux-system -rules: -- apiGroups: - - source.toolkit.fluxcd.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - kustomize.toolkit.fluxcd.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - helm.toolkit.fluxcd.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - notification.toolkit.fluxcd.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - image.toolkit.fluxcd.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - configmaps - - configmaps/status - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: cluster-reconciler-flux-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: -- kind: ServiceAccount - name: kustomize-controller - namespace: flux-system -- kind: ServiceAccount - name: helm-controller - namespace: flux-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: crd-controller-flux-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: crd-controller-flux-system -subjects: -- kind: ServiceAccount - name: kustomize-controller - namespace: flux-system -- kind: ServiceAccount - name: helm-controller - namespace: flux-system -- kind: ServiceAccount - name: source-controller - namespace: flux-system -- kind: ServiceAccount - name: notification-controller - namespace: flux-system -- kind: ServiceAccount - name: image-reflector-controller - namespace: flux-system -- kind: ServiceAccount - name: image-automation-controller - namespace: flux-system ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - control-plane: controller - name: notification-controller - namespace: flux-system -spec: - ports: - - name: http - port: 80 - protocol: TCP - targetPort: http - selector: - app: notification-controller - type: ClusterIP ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - control-plane: controller - name: source-controller - namespace: flux-system -spec: - ports: - - name: http - port: 80 - protocol: TCP - targetPort: http - selector: - app: source-controller - type: ClusterIP ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - control-plane: controller - name: webhook-receiver - namespace: flux-system -spec: - ports: - - name: http - port: 80 - protocol: TCP - targetPort: http-webhook - selector: - app: notification-controller - type: ClusterIP ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - control-plane: controller - name: helm-controller - namespace: flux-system -spec: - replicas: 1 - selector: - matchLabels: - app: helm-controller - template: - metadata: - annotations: - prometheus.io/port: "8080" - prometheus.io/scrape: "true" - labels: - app: helm-controller - spec: - containers: - - args: - - --events-addr=http://notification-controller/ - - --watch-all-namespaces=true - - --log-level=info - - --log-encoding=json - - --enable-leader-election - env: - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - image: ghcr.io/fluxcd/helm-controller:v0.11.0 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz - name: manager - ports: - - containerPort: 8080 - name: http-prom - - containerPort: 9440 - name: healthz - protocol: TCP - readinessProbe: - httpGet: - path: /readyz - port: healthz - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 100m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - volumeMounts: - - mountPath: /tmp - name: temp - nodeSelector: - kubernetes.io/os: linux - serviceAccountName: helm-controller - terminationGracePeriodSeconds: 600 - volumes: - - emptyDir: {} - name: temp ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - control-plane: controller - name: kustomize-controller - namespace: flux-system -spec: - replicas: 1 - selector: - matchLabels: - app: kustomize-controller - template: - metadata: - annotations: - prometheus.io/port: "8080" - prometheus.io/scrape: "true" - labels: - app: kustomize-controller - spec: - containers: - - args: - - --events-addr=http://notification-controller/ - - --watch-all-namespaces=true - - --log-level=info - - --log-encoding=json - - --enable-leader-election - env: - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - image: ghcr.io/fluxcd/kustomize-controller:v0.13.0 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz - name: manager - ports: - - containerPort: 8080 - name: http-prom - - containerPort: 9440 - name: healthz - protocol: TCP - readinessProbe: - httpGet: - path: /readyz - port: healthz - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 100m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - volumeMounts: - - mountPath: /tmp - name: temp - nodeSelector: - kubernetes.io/os: linux - securityContext: - fsGroup: 1337 - serviceAccountName: kustomize-controller - terminationGracePeriodSeconds: 60 - volumes: - - emptyDir: {} - name: temp ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - control-plane: controller - name: notification-controller - namespace: flux-system -spec: - replicas: 1 - selector: - matchLabels: - app: notification-controller - template: - metadata: - annotations: - prometheus.io/port: "8080" - prometheus.io/scrape: "true" - labels: - app: notification-controller - spec: - containers: - - args: - - --watch-all-namespaces=true - - --log-level=info - - --log-encoding=json - - --enable-leader-election - env: - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - image: ghcr.io/fluxcd/notification-controller:v0.15.0 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz - name: manager - ports: - - containerPort: 9090 - name: http - - containerPort: 9292 - name: http-webhook - - containerPort: 8080 - name: http-prom - - containerPort: 9440 - name: healthz - protocol: TCP - readinessProbe: - httpGet: - path: /readyz - port: healthz - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 100m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - volumeMounts: - - mountPath: /tmp - name: temp - nodeSelector: - kubernetes.io/os: linux - serviceAccountName: notification-controller - terminationGracePeriodSeconds: 10 - volumes: - - emptyDir: {} - name: temp ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - control-plane: controller - name: source-controller - namespace: flux-system -spec: - replicas: 1 - selector: - matchLabels: - app: source-controller - strategy: - type: Recreate - template: - metadata: - annotations: - prometheus.io/port: "8080" - prometheus.io/scrape: "true" - labels: - app: source-controller - spec: - containers: - - args: - - --events-addr=http://notification-controller/ - - --watch-all-namespaces=true - - --log-level=info - - --log-encoding=json - - --enable-leader-election - - --storage-path=/data - - --storage-adv-addr=source-controller.$(RUNTIME_NAMESPACE).svc.cluster.local. - env: - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - image: ghcr.io/fluxcd/source-controller:v0.14.0 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz - name: manager - ports: - - containerPort: 9090 - name: http - - containerPort: 8080 - name: http-prom - - containerPort: 9440 - name: healthz - readinessProbe: - httpGet: - path: / - port: http - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 50m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - volumeMounts: - - mountPath: /data - name: data - - mountPath: /tmp - name: tmp - nodeSelector: - kubernetes.io/os: linux - securityContext: - fsGroup: 1337 - serviceAccountName: source-controller - terminationGracePeriodSeconds: 10 - volumes: - - emptyDir: {} - name: data - - emptyDir: {} - name: tmp ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: allow-egress - namespace: flux-system -spec: - egress: - - {} - ingress: - - from: - - podSelector: {} - podSelector: {} - policyTypes: - - Ingress - - Egress ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: allow-scraping - namespace: flux-system -spec: - ingress: - - from: - - namespaceSelector: {} - ports: - - port: 8080 - protocol: TCP - podSelector: {} - policyTypes: - - Ingress ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.15.0 - name: allow-webhooks - namespace: flux-system -spec: - ingress: - - from: - - namespaceSelector: {} - podSelector: - matchLabels: - app: notification-controller - policyTypes: - - Ingress ---- diff --git a/examples/big-bang/template/bigbang/vendor/bigbang/base/flux/kustomization.yaml b/examples/big-bang/template/bigbang/vendor/bigbang/base/flux/kustomization.yaml deleted file mode 100644 index 2b2c2788ea..0000000000 --- a/examples/big-bang/template/bigbang/vendor/bigbang/base/flux/kustomization.yaml +++ /dev/null @@ -1,134 +0,0 @@ -# start with a default flux deployment -resources: -- gotk-components.yaml - -# update flux components to use ironbank images -images: -- name: ghcr.io/fluxcd/helm-controller - newName: registry1.dso.mil/ironbank/fluxcd/helm-controller - newTag: v0.11.0 -- name: ghcr.io/fluxcd/kustomize-controller - newName: registry1.dso.mil/ironbank/fluxcd/kustomize-controller - newTag: v0.13.0 -- name: ghcr.io/fluxcd/notification-controller - newName: registry1.dso.mil/ironbank/fluxcd/notification-controller - newTag: v0.15.0 -- name: ghcr.io/fluxcd/source-controller - newName: registry1.dso.mil/ironbank/fluxcd/source-controller - newTag: v0.14.0 - -patches: - - target: - kind: Deployment - patch: |- - apiVersion: apps/v1 - kind: Deployment - metadata: - name: whatever - spec: - template: - metadata: - annotations: - # Required by Kubernetes node autoscaler - cluster-autoscaler.kubernetes.io/safe-to-evict: "true" - spec: - imagePullSecrets: - - name: private-registry - terminationGracePeriodSeconds: 60 - # Required by Pod Security Policy - securityContext: - runAsUser: 1000 - fsGroup: 1000 - containers: - - name: manager - # Required by Pod Security Policy - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - privileged: false - readOnlyRootFilesystem: true - allowPrivilegeEscalation: false - runAsNonRoot: true - capabilities: - drop: - - ALL - - target: - kind: Deployment - name: helm-controller - patch: |- - apiVersion: apps/v1 - kind: Deployment - metadata: - name: helm-controller - spec: - template: - spec: - containers: - - name: manager - resources: - limits: - cpu: 500m - memory: 750Mi - requests: - cpu: 500m - memory: 750Mi - - target: - kind: Deployment - name: kustomize-controller - patch: |- - apiVersion: apps/v1 - kind: Deployment - metadata: - name: kustomize-controller - spec: - template: - spec: - containers: - - name: manager - resources: - limits: - cpu: 100m - memory: 200Mi - requests: - cpu: 100m - memory: 200Mi - - target: - kind: Deployment - name: notification-controller - patch: |- - apiVersion: apps/v1 - kind: Deployment - metadata: - name: notification-controller - spec: - template: - spec: - containers: - - name: manager - resources: - limits: - cpu: 100m - memory: 100Mi - requests: - cpu: 100m - memory: 100Mi - - target: - kind: Deployment - name: source-controller - patch: |- - apiVersion: apps/v1 - kind: Deployment - metadata: - name: source-controller - spec: - template: - spec: - containers: - - name: manager - resources: - limits: - cpu: 100m - memory: 150Mi - requests: - cpu: 100m - memory: 150Mi diff --git a/examples/big-bang/template/bigbang/vendor/bigbang/base/gitrepository.yaml b/examples/big-bang/template/bigbang/vendor/bigbang/base/gitrepository.yaml deleted file mode 100644 index c2c36ed55d..0000000000 --- a/examples/big-bang/template/bigbang/vendor/bigbang/base/gitrepository.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1beta1 -kind: GitRepository -metadata: - name: bigbang -spec: - ignore: | - # exclude file extensions - /**/*.md - /**/*.txt - /**/*.sh - interval: 10m - url: https://repo1.dso.mil/platform-one/big-bang/bigbang.git - ref: - tag: 1.17.0 diff --git a/examples/big-bang/template/bigbang/vendor/bigbang/base/helmrelease.yaml b/examples/big-bang/template/bigbang/vendor/bigbang/base/helmrelease.yaml deleted file mode 100644 index 49eff03379..0000000000 --- a/examples/big-bang/template/bigbang/vendor/bigbang/base/helmrelease.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2beta1 -kind: HelmRelease -metadata: - name: bigbang -spec: - targetNamespace: bigbang - releaseName: bigbang - interval: 10m - chart: - spec: - chart: chart - sourceRef: - kind: GitRepository - name: bigbang - test: - enable: false - install: - remediation: - retries: -1 - upgrade: - remediation: - retries: 5 - remediateLastFailure: true - cleanupOnFail: true - rollback: - timeout: 10m - cleanupOnFail: false - valuesFrom: - # Optional secret injected with https://repo1.dso.mil/platform-one/big-bang/terraform-modules/big-bang-terraform-launcher - - kind: Secret - name: terraform - optional: true - - kind: Secret - name: common-bb - - kind: ConfigMap - name: common - - kind: Secret - name: environment-bb - - kind: ConfigMap - name: environment \ No newline at end of file diff --git a/examples/big-bang/template/bigbang/vendor/bigbang/base/kustomization.yaml b/examples/big-bang/template/bigbang/vendor/bigbang/base/kustomization.yaml deleted file mode 100644 index 09b812170d..0000000000 --- a/examples/big-bang/template/bigbang/vendor/bigbang/base/kustomization.yaml +++ /dev/null @@ -1,37 +0,0 @@ -configurations: -- transformer.yaml - -namespace: bigbang - -commonLabels: - owner: bigbang - -resources: - - namespace.yaml - - gitrepository.yaml - - helmrelease.yaml - -configMapGenerator: - - name: common - behavior: create - literals: - - values.yaml= - - name: environment - behavior: create - literals: - - values.yaml= - -# Flux combines secrets and configmaps in `valuesFrom`. Kustomize -# cannot distinguish between them when applying suffixes. Therefore, -# the secrets must have different names than the configmaps -# While capital letters or dashes could be used, '-bb' was chosen -# to make the difference obvious at a glance -secretGenerator: - - name: common-bb - behavior: create - literals: - - values.yaml= - - name: environment-bb - behavior: create - literals: - - values.yaml= \ No newline at end of file diff --git a/examples/big-bang/template/bigbang/vendor/bigbang/base/namespace.yaml b/examples/big-bang/template/bigbang/vendor/bigbang/base/namespace.yaml deleted file mode 100644 index 147e8f3920..0000000000 --- a/examples/big-bang/template/bigbang/vendor/bigbang/base/namespace.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: bigbang \ No newline at end of file diff --git a/examples/big-bang/template/bigbang/vendor/bigbang/base/transformer.yaml b/examples/big-bang/template/bigbang/vendor/bigbang/base/transformer.yaml deleted file mode 100644 index f55683c3a5..0000000000 --- a/examples/big-bang/template/bigbang/vendor/bigbang/base/transformer.yaml +++ /dev/null @@ -1,9 +0,0 @@ -nameReference: -- kind: ConfigMap - fieldSpecs: - - path: spec/valuesFrom/name - kind: HelmRelease -- kind: Secret - fieldSpecs: - - path: spec/valuesFrom/name - kind: HelmRelease \ No newline at end of file diff --git a/examples/big-bang/zarf.yaml b/examples/big-bang/zarf.yaml index aa0e914853..ddb7ae4b63 100644 --- a/examples/big-bang/zarf.yaml +++ b/examples/big-bang/zarf.yaml @@ -2,11 +2,18 @@ kind: ZarfPackageConfig metadata: name: big-bang-core-demo description: "Demo Zarf basic deployment of Big Bang core" + # Big Bang / Iron Bank are only amd64 + architecture: amd64 components: - - name: baseline + - name: flux required: true - manifests: manifests/flux + secretName: "private-registry" + manifests: + - name: flux-installer + # This will be built on the package create side and deployed as a regular manifest on package deploy + kustomizations: + - https://repo1.dso.mil/platform-one/big-bang/bigbang.git//base/flux?ref=1.17.0 images: # Flux images - registry1.dso.mil/ironbank/fluxcd/helm-controller:v0.11.0 @@ -29,12 +36,15 @@ components: - name: bb-core required: true + secretName: "private-registry" + manifests: + - name: bb-core-config + kustomizations: + - "kustomizations/bigbang" # 1. helm template bigbang ./chart | yq e '. | select(.kind == "GitRepository") | "- " + .spec.url + "@" + .spec.ref.tag' - # 2. Add the actual bigbang repo as well # https://repo1.dso.mil/platform-one/big-bang/bigbang/-/tags/1.17.0 - manifests: manifests/big-bang repos: - - https://github.com/defenseunicorns/zarf.git - https://repo1.dso.mil/platform-one/big-bang/bigbang.git@1.17.0 - https://repo1.dso.mil/platform-one/big-bang/apps/core/cluster-auditor.git@0.3.0-bb.7 - https://repo1.dso.mil/platform-one/big-bang/apps/core/policy.git@3.5.2-bb.1 diff --git a/examples/data-injection/README.md b/examples/data-injection/README.md index 62d805f368..ecfc4e4b45 100644 --- a/examples/data-injection/README.md +++ b/examples/data-injection/README.md @@ -1,7 +1,146 @@ ## Zarf Appliance Mode Example -This example demonstrates using Zarf in a very low-resources/singlue-use environment. In this mode there is no gitops service and Zarf is simply a standard means of wrapping airgap concerns for K3s. This example deploys a basic K3s cluster using Traefik 2 and configures TLS / airgap concerns to deploy [Podinfo](https://github.com/stefanprodan/podinfo). +This example demonstrates using Zarf in a very low-resources/singlue-use environment. In this mode there is no gitops service and Zarf is simply a standard means of wrapping airgap concerns for K3s. -### Steps to use: -1. Create a Zarf cluster as outlined in the main [README](../../README.md#2-create-the-zarf-cluster) -2. Follow [step 3](../../README.md#3-add-resources-to-the-zarf-cluster) using this config in this folder +# Zarf Data Injection Example + +This example deploys a basic K3s cluster using Traefik 2 and configures TLS / airgap concerns to deploy [Podinfo](https://github.com/stefanprodan/podinfo). +## The Flow + +Here's what you'll do in this example: + +1. [Get ready](#get-ready) + +1. [Create a cluster](#create-a-cluster) + +1. [Package it](#package-it) + +1. [Deploy it](#deploy-it) + +1. [Try it](#try-it) + +1. [Cleanup](#cleanup) + +  + +## Get ready + +Before the magic can happen you have to do a few things: + +1. Install [Docker](https://docs.docker.com/get-docker/). Other container engines will likely work as well but aren't actively tested by the Zarf team. + +2. Install [KinD](https://github.com/kubernetes-sigs/kind). Other Kubernetes distros will work as well, but we'll be using KinD for this example since it is easy and tested frequently and thoroughly. + +3. Clone the Zarf project — for the example configuration files. + +4. Download a Zarf release — you need a binary _**and**_ an init package, [here](../../docs/workstation.md#just-gimmie-zarf). + +  + +## Create a cluster + +You can't run software without _somewhere to run it_, so the first thing to do is create a local Kubernetes cluster that Zarf can deploy to. In this example we'll be using KinD to create a lightweight, local K8s cluster running in Docker. + +Kick that off by running this command: + +```sh +kind create cluster +``` + +This will result in a single-node Kubernetes cluster called `kind-kind` on your local machine running in Docker. Your KUBECONFIG should be automatically configured to talk to the new cluster. + +```sh +cd +zarf init +``` + +Follow the prompts, answering "no" to each of the optional components, since we don't need them for this deployment. + +Congratulations! Your machine is now running a single-node Kubernetes cluster powered by Zarf! + +> _**Note**_ +> +> Zarf supports non-interactive installs too! Give `zarf init --confirm --components logging` a try next time. + +**Troubleshooting:** + +> _**ERROR: Unable to find the package on the local system, expected package at zarf-init.tar.zst**_ +> +> The zarf binary needs an init package to know how to setup your cluster! So, if `zarf init` returns an error like this: +> +> ```sh +> ERROR: Unable to find the package on the local system, expected package at zarf-init.tar.zst +> ``` +> +> It's likely you've either forgotten to download `zarf-init.tar.zst` (as part of [getting ready](#get-ready)) _**OR**_ you are _not_ running `zarf init` from the directory the init package is sitting in. + +> _**ERROR: failed to create cluster: node(s) already exist for a cluster with the name "kind"**_ +> +> You already have a KinD cluster running. Either just move on to use the current cluster, or run `kind delete cluster`, then `kind create cluster`. + +> _**Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?**_ +> +> Docker isn't running or you're otherwise not able to talk to it. Check your Docker installation, then try again. + +  + +## Package it + +Zarf is (at heart) a tool for making it easy to get software from _where you have it_ to _**where you need it**_—specifically, across an airgap. Since moving bits is so core to Zarf the idea of a "ready-to-move group of software" has a specific name—the _package_. + +All of the software a Zarf cluster runs is installed via package—for many reasons like versioning, auditability, etc—which means that if you want to run a in your cluster you're going to have to build a package for it. + +Luckily, this is very easy to do—package contents are defined by simple, declarative yaml files and _we've already made one for you_. To build this package you simply: + +```sh +cd /examples/data-injection # directory with zarf.yaml, and +zarf package create --confirm # make the package +``` + +Watch the terminal scroll for a while. Once things are downloaded & zipped up and you'll see a file ending in `.tar` drop. _That's_ your package. + +*This package ends in .tar instead of .tar.zst because the `zarf.yaml` uncrompressed flag is set to true.* + +  + +## Deploy it + +It's time to feed the package you built into your cluster. + +Since you're running a Zarf cluster directly on your local machine—where this package & `zarf` binary _already are_—deploying the package is very simple: + +```sh +zarf package deploy zarf-package-data-injection-demo.tar --confirm +``` + +In a couple seconds the cluster will have loaded your package. + +  + +## Use it + +This demo should have placed some test files in the cluster from the zarf package. To verify they were created, you can run the following command: + +```shell +kubectl exec -n demo data-injection -- cat /test/this-is-an-example-file.txt +``` + +The output should say: +>This is a sample file to be injected into the cluster. Normal flow would keep this data gitignored as it would likely be large. + + +  + +## Cleanup + +Once you've had your fun it's time to clean up. + +In this case, since the Zarf cluster was installed specifically (and _only_) to serve this example, clean up is really easy—you just tear down the entire cluster: + +```sh +kind delete cluster +``` + +It only takes a couple moments for the _entire cluster_ to disappear—long-running system services and all—leaving your machine ready for the next adventure. + +  diff --git a/examples/data-injection/manifests/data-injection.yaml b/examples/data-injection/manifests/data-injection.yaml index acad2fcf15..19fd501e95 100644 --- a/examples/data-injection/manifests/data-injection.yaml +++ b/examples/data-injection/manifests/data-injection.yaml @@ -1,9 +1,3 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: demo ---- apiVersion: v1 kind: Pod metadata: @@ -14,7 +8,17 @@ metadata: spec: containers: - name: data-injection - image: registry1.dso.mil/ironbank/redhat/ubi/ubi8:8.4 - command: ["/bin/sh", "-ec", "mkdir -p /test && while :; do ls -lah /test; sleep 5 ; done"] - imagePullSecrets: - - name: private-registry + image: alpine:3.15 + command: + [ + "/bin/sh", + "-ec", + "mkdir -p /test && while :; do ls -lah /test; sleep 5 ; done", + ] + resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "128Mi" + cpu: "500m" diff --git a/examples/data-injection/manifests/image-pull-secret.yaml b/examples/data-injection/manifests/image-pull-secret.yaml deleted file mode 100644 index 89c000de16..0000000000 --- a/examples/data-injection/manifests/image-pull-secret.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: demo -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } diff --git a/examples/data-injection/zarf.yaml b/examples/data-injection/zarf.yaml index 73e0f7d022..ee507d4cf4 100644 --- a/examples/data-injection/zarf.yaml +++ b/examples/data-injection/zarf.yaml @@ -22,6 +22,9 @@ data: components: - name: baseline required: true - manifests: manifests + manifests: + - name: example-data-injection-pod + files: + - manifests/data-injection.yaml images: - - registry1.dso.mil/ironbank/redhat/ubi/ubi8:8.4 + - alpine:3.15 diff --git a/examples/game/README.md b/examples/game/README.md index a00c291eec..8940aa30b6 100644 --- a/examples/game/README.md +++ b/examples/game/README.md @@ -2,7 +2,7 @@ This example demonstrates using Zarf to kill time (and evil). -More specifically, you'll be running a copy of the 1993, mega-hit video game _**Doom**_ in a Zarf-installed Kubernetes (k8s) cluster—_right on your local machine_. +More specifically, you'll be running a copy of the 1993, mega-hit video game _**Doom**_ in a Zarf-enabled Kubernetes (k8s) cluster—_right on your local machine_. > _**Note**_ > @@ -13,9 +13,6 @@ More specifically, you'll be running a copy of the 1993, mega-hit video game _** ## The Flow - -asciicast - Here's what you'll do in this example: @@ -36,64 +33,72 @@ Here's what you'll do in this example: ## Get ready - -asciicast - Before the magic can happen you have to do a few things: -1. Get a "root" shell — `zarf` needs power to install stuff / bind ports / etc. +1. Install [Docker](https://docs.docker.com/get-docker/). Other container engines will likely work as well but aren't actively tested by the Zarf team. + +1. Install [KinD](https://github.com/kubernetes-sigs/kind). Other Kubernetes distros will work as well, but we'll be using KinD for this example since it is easy and tested frequently and thoroughly. 1. Clone the Zarf project — for the example configuration files. 1. Download a Zarf release — you need a binary _**and**_ an init package, [here](../../docs/workstation.md#just-gimmie-zarf). -1. Log `zarf` into Iron Bank if you haven't already — instructions [here](../../docs/ironbank.md#2-configure-zarf-the-use-em). +1. (Optional) Log `zarf` into Iron Bank if you haven't already — instructions [here](../../docs/ironbank.md#2-configure-zarf-the-use-em). Optional for this specific example since the container comes from GitHub rather than Iron Bank but a good practice and needed for most of the other examples. -1. Put `zarf` on your path — _technically_ optional but makes running commands simpler. +1. (Optional) Put `zarf` on your path — _technically_ optional but makes running commands simpler. Make sure you are picking the right binary that matches your system architecture. `zarf` for x86 Linux, `zarf-mac-intel` for x86 MacOS, `zarf-mac-apple` for M1 MacOS.   ## Create a cluster - -asciicast - +You can't run software without _somewhere to run it_, so the first thing to do is create a local Kubernetes cluster that Zarf can deploy to. In this example we'll be using KinD to create a lightweight, local K8s cluster running in Docker. + +Kick that off by running this command: -You can't run software without _somewhere to run it_, so the first thing to do is have `zarf` install & run a new, local k8s cluster—the "Zarf cluster". +```sh +kind create cluster +``` -Kick that off by _moving into the directory with your init package_ and running this command: +This will result in a single-node Kubernetes cluster called `kind-kind` on your local machine running in Docker. Your KUBECONFIG should be automatically configured to talk to the new cluster. ```sh cd zarf init ``` -Answer the follow-on prompts as appropriate for your machine configuration & give it a few seconds to run. +Follow the prompts, answering "no" to each of the optional components, since we don't need them for this deployment. -Congratulations! Your machine is now a single node k8s cluster! +Congratulations! Your machine is now running a single-node Kubernetes cluster powered by Zarf! > _**Note**_ > - > Zarf supports fire-and-forget installs too! Give `zarf init --help` a call for more details on that. + > Zarf supports non-interactive installs too! Give `zarf init --confirm --components logging` a try next time. + +**Troubleshooting:** -> _**Error — missing or unreadable package**_ +> _**ERROR: Unable to find the package on the local system, expected package at zarf-init.tar.zst**_ > > The zarf binary needs an init package to know how to setup your cluster! So, if `zarf init` returns an error like this: > ```sh -> FATA[0004] The package archive seems to be missing or unreadable. archive=zarf-init.tar.zst +> ERROR: Unable to find the package on the local system, expected package at zarf-init.tar.zst > ``` > It's likely you've either forgotten to download `zarf-init.tar.zst` (as part of [getting ready](#get-ready)) _**OR**_ you are _not_ running `zarf init` from the directory the init package is sitting in. +> _**ERROR: failed to create cluster: node(s) already exist for a cluster with the name "kind"**_ +> +> You already have a KinD cluster running. Either just move on to use the current cluster, or run `kind delete cluster`, then `kind create cluster`. + +> _**Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?**_ +> +> Docker isn't running or you're otherwise not able to talk to it. Check your Docker installation, then try again. +   ## Package the game - -asciicast - Zarf is (at heart) a tool for making it easy to get software from _where you have it_ to _**where you need it**_—specifically, across an airgap. Since moving bits is so core to Zarf the idea of a "ready-to-move group of software" has a specific name—the _package_. @@ -103,19 +108,16 @@ Luckily, this is very easy to do—package contents are defined by simple, d ```sh cd /examples/game # directory with zarf.yaml, and -zarf package create # make the package +zarf package create --confirm # make the package ``` -Answer the questions & watch the terminal scroll for a while. Once things are downloaded & zipped up and you'll see a file ending in `.tar.zst` drop. _That's_ your package. +Watch the terminal scroll for a while. Once things are downloaded & zipped up and you'll see a file ending in `.tar.zst` drop. _That's_ your package.   ## Deploy it - -asciicast - It's time to feed the package you built into your cluster. @@ -126,104 +128,35 @@ It's time to feed the package you built into your cluster. Since you're running a Zarf cluster directly on your local machine—where the game package & `zarf` binary _already are_—deploying the game is very simple: ```sh -zarf package deploy +zarf package deploy ./zarf-package-appliance-demo-multi-games.tar.zst --confirm ``` -Respond as appropriate and in a couple seconds the cluster will have loaded your package. - -> _**Important**_ -> -> It's possible to try a package deploy _before the Zarf cluster is ready to receive it_. If you see an error like `"https:///v2/": dial tcp ,:443: connect: connection refused;` then it's very likely that you've beat the Zarf startup routines. -> -> The fix is simple: just wait for the cluster to finish starting & try again. +In a couple seconds the cluster will have loaded your package.   - ## Space marine the demon invasion! - -privacy error - - -Navigate your browser to `https://localhost` and be greeted by... a "Privacy error"? What's that about?! - -### Privacy error - -Long-story-short, **it's a false alarm**—your connection _is_ private and _no one_ is trying steal your information. - -Long-story-long, your browser is showing you this because a bare bones, default install of Zarf **generates its own certificate authority (CA)** during install, which it then uses to create certificates to back `https` requests. - -The certificates generated by this CA—and used to secure Zarf cluster services—are perfectly reasonably, secure certificates, it's just that your browser does not recognize the recently-generated CA and so warns you. - ->_**Further reading**_ -> -> If you're interested in this "trusted certificates" + "https" thing, the internet abounds with resources—this [wikipedia article](https://en.wikipedia.org/wiki/Self-signed_certificate) is a great place to start. - -  - -### Proceed anyway - - -proceed anyway - - -With an understanding of _why there is no danger here_, you can proceed to the example after some one-time (per Zarf cluster) browser "risk" acceptance: - -1. Click the "Not secure" warning (at the left of the location bar) to view the site security information. - -1. Click the "Certificate is not valid" row to show the certificate information popup. - -1. Verify the certificate has: - - - an "Issued By" block showing the **Zarf Private Certificate Authority** issuer, and - - - a "Validity Period" block showing that the certificate is not expired. - - Once you're comfortable with the Zarf certificate, you can close the certificate popup. - -1. To tell the browser that _you trust the Zarf-issued certificate_, click the "Advanced" button to show the advanced options, and then - -1. Click the "Proceed to localhost" link. - -Now—in this and all future requests to this URL—you'll be taken directly to the example service. - -  - -### It begins! - - -dosbox - - -Give the example a couple of seconds to "boot up". +After the deploy has completed, a prompt would have displayed the new connect commands you can use to connect automatically bring up the game in your browser. Running the command `zarf connect games` should open your browser to `http://localhost:` and be greeted by a short catalog of games to play. Run `zarf connect doom` to directly open the _**Doom**_ game. We use `zarf connect` here so we can connect to it in a browser without needing a Kubernetes Ingress Controller, which is a more advanced topic and has different configurations depending on which controller and which distribution of Kubernetes you are using. Once you see the ultra-iconic title screen, you're ready to go (save the world)! -  - -> _**Note**_ -> -> The images / steps described here are for Chrome but all major, modern browsers will have a similar security mechanism and associated workaround. +![game](img/game.png)   - ## Cleanup - -asciicast - Once you've had your fun it's time to clean up. In this case, since the Zarf cluster was installed specifically (and _only_) to serve this example, clean up is really easy—you just tear down the entire cluster: ```sh -zarf destroy +kind delete cluster ``` -It only takes a couple moments for the _entire Zarf cluster_ to disappear—long-running system services and all—leaving your machine ready for the next adventure. +It only takes a couple moments for the _entire cluster_ to disappear—long-running system services and all—leaving your machine ready for the next adventure.   diff --git a/examples/game/add-logging.md b/examples/game/add-logging.md index 7bc850202e..fcaee1bae7 100644 --- a/examples/game/add-logging.md +++ b/examples/game/add-logging.md @@ -1,18 +1,13 @@ # Zarf Components - Add Logging -This example demonstrates using a [Zarf component](./components.md) to inject zero-config, centralized logging into your Zarf cluster. +This example demonstrates using a Zarf component to inject zero-config, centralized logging into your Zarf cluster. More specifically, you'll be adding a [Promtail / Loki / Grafana (PLG)](https://github.com/grafana/loki) stack to the example game cluster by installing Zarf's "logging" component.   - ## The Flow - -asciicast - - Here's what you'll do in this example: 1. [Get ready](#get-ready) @@ -32,27 +27,18 @@ Here's what you'll do in this example: ## Get ready - -asciicast - - This scenario builds upon the previous one, so: -1. Run through the [Zarf game example](./README.md) again but _**don't** do the cleanup step_ — you're setup correctly once you can pull the game up in your browser. +1. Run through the [Zarf game example](./README.md) but stop when you're told to run `zarf init` -1. Take a deep breath—because it's good for your body—and read on! +1. Take a deep breath—because it's good for your body—and read on!     - ## Install the logging component - -asciicast - - Installing a Zarf component is _really_ easy—you just have to let `zarf init` know that you want use it. That's it! Exactly like when you first created the game example cluster, you _move into the directory holding your init package_ and run: @@ -66,17 +52,12 @@ You can answer the follow-on prompts in almost the exact same way as during your Give it some time for the new logging pods to come up and you're ready to go! - > _**Note**_ - > - > You can install components as part of new cluster installs too (obviously)—there's no need to update afterward if you already know you need a component. - > _**Note**_ > > Zarf supports non-interactive installs too! See `zarf init --help` for how to make that work.   - ## Note the credentials Go back to your terminal and review the `zarf init` command output—the very last thing printed should be a set of credentials Zarf has generated for you. @@ -85,29 +66,22 @@ Pay attention to these because you're going to need them to log into your shiny, The line you want will look something like this: -```sh -WARN[0026] Credentials stored in ~/.git-credentials Gitea Username (if installed)=zarf-git-user Grafana Username=zarf-admin Password (all)="AbCDe0fGH12IJklMnOPQRSt~uVWx" -``` +![logging-creds](./img/logging-creds.png) -Pull out the `Grafana Username` and `Password (all)` values & save them for later. +The ones under "Logging" are what you'll need.   ## Check the logs - -asciicast - - We've only _just_ installed the logging utilities so we (likely) haven't had time to record anything interesting. Since log aggregation & monitoring aren't worth much without something to collect, let's get some data in there.   - ### Generate some traffic -Pull up the game in your brower—_[instructions here](./README.md#space-marine-the-demon-invasion), in case you forgot how_—and then reload the browser window a few times. +Deploy the Game example again, then pull up the game in your brower—_[instructions here](./README.md#space-marine-the-demon-invasion), in case you forgot how_—and then reload the browser window a few times. Doing that sends a bunch of HTTP traffic into the cluster & should give you something worth looking at in Grafana. @@ -116,19 +90,15 @@ Doing that sends a bunch of HTTP traffic into the cluster & should give you some ### Get into Grafana - -dosbox - - Now that you've got some logs worth looking at, you're ready to log into your brand new Grafana instance. -Get started by navigating your browser to: `https://localhost/monitor/explore`. +Get started by opening Grafana using `zarf connect logging` You'll be redirected the `/login` page where you have to sign in with the Grafana credentials you saved [in a previous step](#note-the-credentials). -Once you've successfully logged in you will be redirected back to: +Once you've successfully logged in go to: -1. the `monitor/explore` page, where +1. The "Explore" page (Button on the left that looks like a compass) 1. you can select `Loki` in the dropdown, and then @@ -141,16 +111,12 @@ Submit that query and you'll get back a dump of all the game pod logs that Loki ## Cleanup - -asciicast - - Once you've had your fun it's time to clean up. In this case, since the Zarf cluster was installed specifically (and _only_) to serve this example, clean up is really easy—you just tear down the entire cluster: ```sh -zarf destroy --confirm +kind delete cluster ``` -It takes just a couple moments for the _entire Zarf cluster_ to disappear—long-running system services and all—leaving your machine squeaky clean. +It takes just a couple moments for the _entire cluster_ to disappear—long-running system services and all—leaving your machine squeaky clean. diff --git a/examples/game/image/Dockerfile b/examples/game/image/Dockerfile index 6f753a111a..f7ae42d224 100644 --- a/examples/game/image/Dockerfile +++ b/examples/game/image/Dockerfile @@ -1,21 +1,31 @@ FROM alpine:latest -ARG GAME_URL -ARG GAME_ARGS +WORKDIR /binary +RUN apk add gcc musl-dev && \ + wget -O darkhttpd.c https://raw.githubusercontent.com/emikulic/darkhttpd/master/darkhttpd.c && \ + cc -static -Os -o darkhttpd darkhttpd.c WORKDIR /site RUN wget https://js-dos.com/6.22/current/js-dos.js && \ wget https://js-dos.com/6.22/current/wdosbox.js && \ - wget https://js-dos.com/6.22/current/wdosbox.wasm.js && \ - wget -O game.zip "$GAME_URL" + wget https://js-dos.com/6.22/current/wdosbox.wasm.js -COPY index.html . -RUN sed -i s/GAME_ARGS/$GAME_ARGS/ index.html +RUN wget -O aladdin.zip "https://web.archive.org/web/20190303222445if_/https://www.dosgames.com/files/DOSBOX_ALADDIN.ZIP" +RUN wget -O doom.zip "https://archive.org/download/DoomsharewareEpisode/doom.ZIP" +RUN wget -O mario-brothers.zip "https://image.dosgamesarchive.com/games/mario-bro.zip" +RUN wget -O prince-of-persia.zip "https://web.archive.org/web/20181030180256if_/http://image.dosgamesarchive.com/games/pop1.zip" +RUN wget -O quake.zip "https://web.archive.org/web/20190303223506if_/https://www.dosgames.com/files/DOSBOX_QUAKE.ZIP" +RUN wget -O warcraft-ii.zip "https://web.archive.org/web/20190303222732if_/https://www.dosgames.com/files/DOSBOX_WAR2.ZIP" -WORKDIR /binary -RUN apk add gcc musl-dev && \ - wget -O darkhttpd.c https://raw.githubusercontent.com/emikulic/darkhttpd/master/darkhttpd.c && \ - cc -static -Os -o darkhttpd darkhttpd.c +RUN wget -O aladdin.png "https://image.dosgamesarchive.com/screenshots/aladdem-4.png" && \ + wget -O doom.png "https://image.dosgamesarchive.com/screenshots/doom01.png" && \ + wget -O mario-brothers.png "https://image.dosgamesarchive.com/screenshots/marionl-6.png" && \ + wget -O prince-of-persia.png "https://image.dosgamesarchive.com/screenshots/prince102.png" && \ + wget -O quake.png "https://image.dosgamesarchive.com/screenshots/quake13.png" && \ + wget -O warcraft-ii.png "https://image.dosgamesarchive.com/screenshots/war2demo3.png" + + +COPY index.html . FROM scratch COPY --from=0 /site /site @@ -24,4 +34,4 @@ COPY --from=0 /binary /binary WORKDIR /site ENTRYPOINT ["/binary/darkhttpd", "/site", "--port", "8000"] -# docker build -t registry.dso.mil/platform-one/big-bang/apps/product-tools/zarf/game:doom --build-arg GAME_URL=https://archive.org/download/DoomsharewareEpisode/doom.ZIP --build-arg GAME_ARGS=\"DOOM.EXE\" . +# docker buildx build --push --platform linux/arm/v7,linux/arm64/v8,linux/amd64 --tag defenseunicorns/zarf-game:multi-tile . diff --git a/examples/game/image/index.html b/examples/game/image/index.html index 4040696d61..45731e9ee4 100644 --- a/examples/game/image/index.html +++ b/examples/game/image/index.html @@ -1,23 +1,115 @@ - + + + Zarf needs games too + + + - - Zarf needs games too - - - - - - - + } + + + +
+
+ + +
+
+ + +
+
+ + +
+
\ No newline at end of file diff --git a/examples/game/img/game.png b/examples/game/img/game.png new file mode 100644 index 0000000000..19aa703915 Binary files /dev/null and b/examples/game/img/game.png differ diff --git a/examples/game/img/logging-creds.png b/examples/game/img/logging-creds.png new file mode 100644 index 0000000000..2a78369db5 Binary files /dev/null and b/examples/game/img/logging-creds.png differ diff --git a/examples/game/manifests/deployment.yaml b/examples/game/manifests/deployment.yaml new file mode 100644 index 0000000000..5c2038636b --- /dev/null +++ b/examples/game/manifests/deployment.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: game +spec: + selector: + matchLabels: + app: game + template: + metadata: + labels: + app: game + spec: + containers: + - name: multi-game + image: "defenseunicorns/zarf-game:multi-tile-dark" + ports: + - name: http + containerPort: 8000 + protocol: TCP + resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "128Mi" + cpu: "500m" diff --git a/examples/game/manifests/game.yaml b/examples/game/manifests/game.yaml deleted file mode 100644 index 431dadb803..0000000000 --- a/examples/game/manifests/game.yaml +++ /dev/null @@ -1,58 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: demo-ingress - namespace: default - annotations: - kubernetes.io/ingress.class: "traefik" - traefik.ingress.kubernetes.io/router.middlewares: kube-system-ssl-redirect@kubernetescrd -spec: - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: game - port: - number: 8000 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: game - namespace: default -spec: - selector: - matchLabels: - app: game - template: - metadata: - labels: - app: game - spec: - containers: - - name: game - image: registry.dso.mil/platform-one/big-bang/apps/product-tools/zarf/game:doom - ports: - - name: http - containerPort: 8000 - protocol: TCP - imagePullSecrets: - - name: private-registry ---- -apiVersion: v1 -kind: Service -metadata: - name: game - namespace: default -spec: - type: ClusterIP - selector: - app: game - ports: - - name: http - port: 8000 - protocol: TCP - targetPort: 8000 diff --git a/examples/game/manifests/image-pull-secret.yaml b/examples/game/manifests/image-pull-secret.yaml deleted file mode 100644 index 38ffb35c9c..0000000000 --- a/examples/game/manifests/image-pull-secret.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: default -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } diff --git a/examples/game/manifests/ingress.yaml b/examples/game/manifests/ingress.yaml new file mode 100644 index 0000000000..21a7832350 --- /dev/null +++ b/examples/game/manifests/ingress.yaml @@ -0,0 +1,15 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: demo-ingress +spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: game + port: + number: 8000 diff --git a/examples/game/manifests/service.yaml b/examples/game/manifests/service.yaml new file mode 100644 index 0000000000..5e830d4a81 --- /dev/null +++ b/examples/game/manifests/service.yaml @@ -0,0 +1,38 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: doom + annotations: + zarf.dev/connect-description: "Play doom!!!" + zarf.dev/connect-url: "?doom" + labels: + # Enables "zarf connect doom" + zarf.dev/connect-name: doom +spec: + selector: + app: game + ports: + - name: http + port: 8000 + protocol: TCP + targetPort: 8000 +--- +apiVersion: v1 +kind: Service +metadata: + name: game + annotations: + zarf.dev/connect-description: "Play some old dos games 🦄" + labels: + # Enables "zarf connect games" + zarf.dev/connect-name: games +spec: + selector: + app: game + ports: + - name: http + port: 8000 + protocol: TCP + targetPort: 8000 + diff --git a/examples/game/zarf.yaml b/examples/game/zarf.yaml index 85c7bcdc72..1709d39e55 100644 --- a/examples/game/zarf.yaml +++ b/examples/game/zarf.yaml @@ -1,12 +1,16 @@ kind: ZarfPackageConfig metadata: - name: appliance-demo-doom - description: "Demo Zarf appliance mode with doom game" + name: appliance-demo-multi-games + description: "Demo Zarf appliance mode with some dos games" components: - name: baseline required: true - manifests: manifests - + manifests: + - name: multi-games + files: + - manifests/ingress.yaml + - manifests/deployment.yaml + - manifests/service.yaml images: - - registry.dso.mil/platform-one/big-bang/apps/product-tools/zarf/game:doom + - defenseunicorns/zarf-game:multi-tile-dark diff --git a/examples/gitops-data/README.md b/examples/gitops-data/README.md index 652e4c0b1c..354a19fc01 100644 --- a/examples/gitops-data/README.md +++ b/examples/gitops-data/README.md @@ -7,10 +7,10 @@ choice. ## Demonstrated Features -### Docker Image Deployment +### Docker Image Push -This example demonstrates using component `images` to deploy container images -to a docker container image registry. Images provided to the `images` tag are +This example demonstrates using component `images` to push container images +to an image registry. Images provided to the `images` tag are uploaded to a Zarf hosted docker registry, which can be later used by Kubernetes manifests, or manually used as shown in this guide. @@ -67,17 +67,31 @@ container images to the Docker registry. zarf package deploy zarf-package-gitops-service-data.tar.zst ``` -> _**Important**_ -> -> It's possible to try a package deploy _before the Zarf cluster is ready to receive it_. If you see an error like `"https:///v2/": dial tcp ,:443: connect: connection refused;` then it's very likely that you've beat the Zarf startup routines. -> -> The fix is simple: just wait for the cluster to finish starting & try again. - ## Applying the Kustomization Once the package has been deployed, the Kustomization can be applied from the Gitea repository using the below command. ```sh -kubectl apply -k https://zarf-git-user:$(./zarf tools get-admin-password)@localhost/zarf-git-user/mirror__github.com__stefanprodan__podinfo//kustomize +# Run 'zarf connect' and send it to the background +zarf connect git& + +# Apply the kustomization +kubectl apply -k http://zarf-git-user:$(zarf tools get-admin-password)@localhost:/zarf-git-user/mirror__github.com__stefanprodan__podinfo//kustomize + +# Inspect +zarf tools k9s + +# Bring the connection back to the foreground +fg + +# Kill the connection with Ctrl-C +``` + +## Clean Up + +Clean up simply by just deleting the whole cluster + +```sh +kind delete cluster ``` diff --git a/examples/gitops-data/zarf.yaml b/examples/gitops-data/zarf.yaml index 4753f3fe3a..c0687a1a6b 100644 --- a/examples/gitops-data/zarf.yaml +++ b/examples/gitops-data/zarf.yaml @@ -9,7 +9,7 @@ components: images: - ghcr.io/stefanprodan/podinfo:6.0.0 repos: - # Do a tag-provided Git Repo mirror + # Do a tag-provided Git Repo mirror - https://github.com/defenseunicorns/zarf.git@v0.12.0 # Do a tag-provided Git Repo mirror with the default branch of main - https://repo1.dso.mil/platform-one/big-bang/apps/security-tools/twistlock.git@0.0.9-bb.0 diff --git a/examples/k3d.yaml b/examples/k3d.yaml new file mode 100644 index 0000000000..e1c64fcac8 --- /dev/null +++ b/examples/k3d.yaml @@ -0,0 +1,9 @@ +# usage: +# create: k3d cluster create --config k3d.yaml +# delete: k3d cluster delete --config k3d.yaml +# Schema compains but cli complains with v2... ¯\_(ツ)_/¯ +apiVersion: k3d.io/v1alpha3 +kind: Simple +name: zarf-test +servers: 1 +agents: 2 diff --git a/examples/kind.yaml b/examples/kind.yaml new file mode 100644 index 0000000000..30e30269a8 --- /dev/null +++ b/examples/kind.yaml @@ -0,0 +1,10 @@ +# usage: +# create: kind create cluster --config kind.yaml +# delete: kind delete cluster --name=zarf-test +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +name: zarf-test +nodes: + - role: control-plane + - role: worker + - role: worker diff --git a/examples/postgres-operator/README.md b/examples/postgres-operator/README.md index 1ce58a767c..21ecec7365 100644 --- a/examples/postgres-operator/README.md +++ b/examples/postgres-operator/README.md @@ -8,22 +8,51 @@ After looking at several alternatives, Zalando's postgres operator felt like the ## Prerequisites -1. Install [Vagrant](https://www.vagrantup.com/) -2. Install `make` and `kustomize` -1. Install `sha256sum` (on Mac it's `brew install coreutils`) +1. Install [Docker](https://docs.docker.com/get-docker/). Other container engines will likely work as well but aren't actively tested by the Zarf team. + +1. Install [KinD](https://github.com/kubernetes-sigs/kind). Other Kubernetes distros will work as well, but we'll be using KinD for this example since it is easy and tested frequently and thoroughly. + +1. Clone the Zarf project — for the example configuration files. + +1. Download a Zarf release — you need a binary _**and**_ an init package, [here](../../docs/workstation.md#just-gimmie-zarf). + +1. Log `zarf` into Iron Bank if you haven't already — instructions [here](../../docs/ironbank.md#2-configure-zarf-the-use-em). Optional for this specific example since the container comes from GitHub rather than Iron Bank but a good practice and needed for most of the other examples. + +1. (Optional) Put `zarf` on your path — _technically_ optional but makes running commands simpler. Make sure you are picking the right binary that matches your system architecture. `zarf` for x86 Linux, `zarf-mac-intel` for x86 MacOS, `zarf-mac-apple` for M1 MacOS. + +1. Create a Zarf cluster as described in the [Doom example docs](../game/README.md) ## Instructions -1. `cd examples/postgres-operator` -1. Run one of these two commands: - - `make all` - Download the latest version of Zarf, build the deploy package, and start a VM with Vagrant - - `make all-dev` - Build Zarf locally, build the deploy package, and start a VM with Vagrant -1. Run: `./zarf init --confirm --components management --host 127.0.0.1` - Initialize Zarf, telling it to install just the management component, and tells Zarf to use `127.0.0.1` as the hostname. If you want to use interactive mode instead just run `./zarf init`. -1. Wait a bit, run `k9s` to see pods come up. Don't move on until everything is running -1. Run: `./zarf package deploy zarf-package-postgres-operator-demo.tar.zst --confirm` - Deploy the package. If you want interactive mode instead just run `./zarf package deploy`, it will give you a picker to choose the package. -1. Wait a couple of minutes. Run `k9s` to watch progress -1. The Postgres Operator UI will be available at [https://postgres-operator-ui.localhost:8443](https://postgres-operator-ui.localhost:8443) and PGAdmin will be available at [https://pgadmin.localhost:8443](https://pgadmin.localhost:8443). -1. Set up a server in PGAdmin: +### Deploy the package + +```sh +# Open the directory +cd examples/postgres-operator + +# Build the package +zarf package create + +# Deploy the package (Press TAB for the listing of available packages) +zarf package deploy +``` + +Wait a couple of minutes. You'll know it is done when Zarf exits and you get the 3 connect commands. + +### Create the backups bucket in MinIO (TODO: Figure out how to create the bucket automatically) + +1. Run `zarf connect minio` to navigate to the web console. +1. Log in - Username: `minio` - Password: `minio123` +1. Buckets -> Create Bucket + - Bucket Name: `postgres-operator-backups` + +### Open the UI + +The Postgres Operator UI will be available by running `./zarf connect postgres-operator-ui` and pgadmin will be available by running `./zarf connect pgadmin` + +> If you want to run other commands after/during the browsing of the postgres tools, you can add a `&` character at the end of the connect command to run the command in the background ie) `./zarf connect pgadmin &`. + +### Set up a server in PGAdmin: - General // Name: `acid-zarf-test` - General // Server group: `Servers` - Connection // Host: (the URL in the table below) @@ -32,23 +61,21 @@ After looking at several alternatives, Zalando's postgres operator felt like the - Connection // Username: `zarf` - Connection // Password: (run the command in the table below) - SSL // SSL mode: `Require` -1. Create the backups bucket in MinIO (TODO: Figure out how to create the bucket automatically) - 1. Navigate to [https://minio-console.localhost:8443](https://minio-console.localhost:8443) - 1. Log in - Username: `minio` - Password: `minio123` - 1. Buckets -> Create Bucket - - Bucket Name: `postgres-operator-backups` -1. When you're done, run `exit` to leave the VM then `make vm-destroy` to bring everything down +### Clean Up +```sh +kind delete cluster +``` ## Logins | Service | URL | Username | Password | | ------------------------- | ------------------------------------------------------------------------------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Postgres Operator UI | [https://postgres-operator-ui.localhost:8443](https://postgres-operator-ui.localhost:8443) | N/A | N/A | -| PGAdmin | [https://pgadmin.localhost:8443](https://pgadmin.localhost:8443) | `zarf@example.local` | Run: `zarf tools get-admin-password` | +| Postgres Operator UI | `zarf connect postgres-operator-ui` | N/A | N/A | +| PGAdmin | `zarf connect pgadmin` | `zarf@example.local` | Run: `zarf tools get-admin-password` | | Example Postgres Database | `acid-zarf-test.postgres-operator.svc.cluster.local` | `zarf` | Run: `echo $(kubectl get secret zarf.acid-zarf-test.credentials.postgresql.acid.zalan.do -n postgres-operator --template={{.data.password}} \| base64 -d)` | -| Minio Console | [https://minio-console.localhost:8443](https://minio-console.localhost:8443) | `minio` | `minio123` | +| Minio Console | `zarf connect minio` | `minio` | `minio123` | ## References - https://blog.flant.com/comparing-kubernetes-operators-for-postgresql/ diff --git a/examples/postgres-operator/manifests/000-namespaces.yaml b/examples/postgres-operator/manifests/000-namespaces.yaml deleted file mode 100644 index cfaefb1018..0000000000 --- a/examples/postgres-operator/manifests/000-namespaces.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: postgres-operator ---- -apiVersion: v1 -kind: Namespace -metadata: - name: minio-operator diff --git a/examples/postgres-operator/manifests/image-pull-secret.yaml b/examples/postgres-operator/manifests/image-pull-secret.yaml deleted file mode 100644 index 291d51c9d0..0000000000 --- a/examples/postgres-operator/manifests/image-pull-secret.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: minio-operator -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry.opensource.zalan.do": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } ---- -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: postgres-operator -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry.opensource.zalan.do": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } diff --git a/examples/postgres-operator/manifests/minio-instance-zarf-connect.yaml b/examples/postgres-operator/manifests/minio-instance-zarf-connect.yaml new file mode 100644 index 0000000000..8aad5d50e4 --- /dev/null +++ b/examples/postgres-operator/manifests/minio-instance-zarf-connect.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: minio-console-zarf-connect + namespace: minio-operator + annotations: + zarf.dev/connect-description: "Launch the minio console" + labels: + zarf.dev/connect-name: minio +spec: + selector: + v1.min.io/tenant: zarf-minio-instance + ports: + - name: http-console + port: 9090 + protocol: TCP + targetPort: 9090 diff --git a/examples/postgres-operator/manifests/minio-instance.yaml b/examples/postgres-operator/manifests/minio-instance.yaml deleted file mode 100644 index d161b5cb86..0000000000 --- a/examples/postgres-operator/manifests/minio-instance.yaml +++ /dev/null @@ -1,64 +0,0 @@ -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: minio-instance - namespace: minio-operator -spec: - chart: https://%{KUBERNETES_API}%/static/charts/minio-instance-4.2.3-bb.1.tgz - targetNamespace: minio-operator - # https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio/-/blob/4.2.3-bb.1/chart/values.yaml - valuesContent: |- - hostname: minio.localhost - tenants: - pools: - ## Servers specifies the number of MinIO Tenant Pods / Servers in this pool. - ## For standalone mode, supply 1. For distributed mode, supply 4 or more. - ## Note that the operator does not support upgrading from standalone to distributed mode. - - servers: 1 - ## volumesPerServer specifies the number of volumes attached per MinIO Tenant Pod / Server. - volumesPerServer: 4 - ## size specifies the capacity per volume - size: 1Gi - ## storageClass specifies the storage class name to be used for this pool - storageClassName: local-path - ## Used to specify a toleration for a pod - tolerations: {} - ## nodeSelector parameters for MinIO Pods. It specifies a map of key-value pairs. For the pod to be - ## eligible to run on a node, the node must have each of the - ## indicated key-value pairs as labels. - ## Read more here: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - nodeSelector: {} - ## Affinity settings for MinIO pods. Read more about affinity - ## here: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity. - affinity: {} - ## Configure resource requests and limits for MinIO containers - resources: - requests: - cpu: "250m" - memory: "1Gi" - limits: - cpu: "500m" - memory: "1Gi" - ## Configure security context - ## BB Note: Defaults for Ironbank image are 1001 for user, group, and fsGroup - securityContext: - runAsUser: 1001 - runAsGroup: 1001 - fsGroup: 1001 - console: - enabled: true ---- -apiVersion: traefik.containo.us/v1alpha1 -kind: IngressRoute -metadata: - name: minio-console-ingressroute - namespace: minio-operator -spec: - entryPoints: - - websecure - routes: - - match: Host(`minio-console.localhost`) - kind: Rule - services: - - name: minio-instance-console - port: 9090 diff --git a/examples/postgres-operator/manifests/minio-operator.yaml b/examples/postgres-operator/manifests/minio-operator.yaml deleted file mode 100644 index 8736dbe7fa..0000000000 --- a/examples/postgres-operator/manifests/minio-operator.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: minio-operator - namespace: minio-operator -spec: - chart: https://%{KUBERNETES_API}%/static/charts/minio-operator-4.2.3-bb.1.tgz - targetNamespace: minio-operator - # https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio-operator/-/blob/4.2.3-bb.1/chart/values.yaml - valuesContent: |- - imagePullSecrets: - - name: private-registry - operator: - image: - repository: registry1.dso.mil/ironbank/opensource/minio/operator - tag: v4.2.3 - resources: - requests: - cpu: 200m - memory: 256Mi - ephemeral-storage: 500Mi - limits: - cpu: 200m - memory: 256Mi diff --git a/examples/postgres-operator/manifests/patch-svc-accounts.yaml b/examples/postgres-operator/manifests/patch-svc-accounts.yaml new file mode 100644 index 0000000000..06cb13de91 --- /dev/null +++ b/examples/postgres-operator/manifests/patch-svc-accounts.yaml @@ -0,0 +1,6 @@ +# This will make zarf aware of this SA to do the imagepullsecret patching for it +apiVersion: v1 +kind: ServiceAccount +metadata: + name: postgres-pod + namespace: postgres-operator \ No newline at end of file diff --git a/examples/postgres-operator/manifests/pgadmin-zarf-connect.yaml b/examples/postgres-operator/manifests/pgadmin-zarf-connect.yaml new file mode 100644 index 0000000000..131c343d61 --- /dev/null +++ b/examples/postgres-operator/manifests/pgadmin-zarf-connect.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: pgadmin-zarf-connect + namespace: postgres-operator + annotations: + zarf.dev/connect-description: "Launch the pgadmin web interface" + labels: + zarf.dev/connect-name: pgadmin +spec: + selector: + app.kubernetes.io/instance: zarf-pgadmin4 + app.kubernetes.io/name: pgadmin4 + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 80 diff --git a/examples/postgres-operator/manifests/pgadmin.yaml b/examples/postgres-operator/manifests/pgadmin.yaml deleted file mode 100644 index 24d7a29982..0000000000 --- a/examples/postgres-operator/manifests/pgadmin.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: pgadmin4 - namespace: postgres-operator -spec: - chart: https://%{KUBERNETES_API}%/static/charts/pgadmin4-1.7.2.tgz - targetNamespace: postgres-operator - # https://github.com/rowanruseler/helm-charts/blob/master/charts/pgadmin4/values.yaml - valuesContent: |- - # image: - # registry: registry1.dso.mil - # repository: ?? - # tag: ?? - imagePullSecrets: - - name: private-registry - serviceAccount: - create: true - persistentVolume: - size: 2Gi - resources: - requests: - cpu: "100m" - memory: "256Mi" - limits: - cpu: "500m" - memory: "512Mi" - env: - email: "zarf@example.local" - password: "###ZARF_SECRET###" ---- -apiVersion: traefik.containo.us/v1alpha1 -kind: IngressRoute -metadata: - name: pgadmin-ingressroute - namespace: postgres-operator -spec: - entryPoints: - - websecure - routes: - - match: Host(`pgadmin.localhost`) - kind: Rule - services: - - name: pgadmin4 - port: 80 diff --git a/examples/postgres-operator/manifests/postgres-operator-ui-zarf-connect.yaml b/examples/postgres-operator/manifests/postgres-operator-ui-zarf-connect.yaml new file mode 100644 index 0000000000..dc309177db --- /dev/null +++ b/examples/postgres-operator/manifests/postgres-operator-ui-zarf-connect.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: postgres-operator-ui-zarf-connect + namespace: postgres-operator + annotations: + zarf.dev/connect-description: "Launch the postgres opertor web interface" + labels: + zarf.dev/connect-name: postgres-operator-ui +spec: + selector: + app.kubernetes.io/instance: zarf-postgres-operator-ui + app.kubernetes.io/name: postgres-operator-ui + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 8081 diff --git a/examples/postgres-operator/manifests/postgres-operator-ui.yaml b/examples/postgres-operator/manifests/postgres-operator-ui.yaml deleted file mode 100644 index c17b220b49..0000000000 --- a/examples/postgres-operator/manifests/postgres-operator-ui.yaml +++ /dev/null @@ -1,63 +0,0 @@ -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: postgres-operator-ui - namespace: postgres-operator -spec: - chart: https://%{KUBERNETES_API}%/static/charts/postgres-operator-ui-1.7.0.tgz - targetNamespace: postgres-operator - # https://github.com/zalando/postgres-operator/blob/v1.7.0/charts/postgres-operator-ui/values.yaml - valuesContent: |- - # image: - # registry: registry1.dso.mil - # repository: ?? - # tag: ?? - imagePullSecrets: - - name: private-registry - resources: - requests: - cpu: "100m" - memory: "100Mi" - limits: - cpu: "200m" - memory: "200Mi" - envs: - # IMPORTANT: While operator chart and UI chart are idendependent, this is the interface between - # UI and operator API. Insert the service name of the operator API here! - operatorApiUrl: "http://postgres-operator:8080" - operatorClusterNameLabel: "cluster-name" - resourcesVisible: "False" - targetNamespace: "postgres-operator" - teams: - - "acid" - extraEnvs: - - name: WALE_S3_ENDPOINT - value: "http+path://minio.minio-operator.svc.cluster.local:80" - - name: AWS_ENDPOINT - value: "http://minio.minio-operator.svc.cluster.local" - - name: SPILO_S3_BACKUP_PREFIX - value: "spilo/" - - name: AWS_ACCESS_KEY_ID - value: "minio" - - name: AWS_SECRET_ACCESS_KEY - value: "minio123" - - name: SPILO_S3_BACKUP_BUCKET - value: "postgres-operator-backups" - # We are defining our own Ingress manifest - ingress: - enabled: false ---- -apiVersion: traefik.containo.us/v1alpha1 -kind: IngressRoute -metadata: - name: postgres-operator-ui-ingressroute - namespace: postgres-operator -spec: - entryPoints: - - websecure - routes: - - match: Host(`postgres-operator-ui.localhost`) - kind: Rule - services: - - name: postgres-operator-ui - port: 80 diff --git a/examples/postgres-operator/manifests/postgres-operator.yaml b/examples/postgres-operator/manifests/postgres-operator.yaml index 1b3a7a9271..85a873a39b 100644 --- a/examples/postgres-operator/manifests/postgres-operator.yaml +++ b/examples/postgres-operator/manifests/postgres-operator.yaml @@ -1,62 +1,3 @@ -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: postgres-operator - namespace: postgres-operator -spec: - chart: https://%{KUBERNETES_API}%/static/charts/postgres-operator-1.7.0.tgz - targetNamespace: postgres-operator - # https://github.com/zalando/postgres-operator/blob/v1.7.0/charts/postgres-operator/values.yaml - valuesContent: |- - # image: - # Eventually we'll need this to come from Iron Bank - # registry: registry1.dso.mil - # repository: ?? - # tag: ?? - # configGeneral: - # docker_image: registry1.dso.mil/.../spilo-13:2.1-p1 - imagePullSecrets: - - name: private-registry - configPostgresPodResources: - default_cpu_request: "100m" - default_memory_request: "100Mi" - default_cpu_limit: "500m" - default_memory_limit: "500Mi" - min_cpu_limit: "250m" - min_memory_limit: "250Mi" - configAwsOrGcp: - wal_s3_bucket: "postgres-operator-backups" - configLogicalBackup: - # logical_backup_docker_image: "registry1.dso.mil/.../logical-backup:v1.7.0" - logical_backup_s3_endpoint: "http://minio.minio-operator.svc.cluster.local" - logical_backup_s3_access_key_id: "minio" - logical_backup_s3_bucket: "postgres-operator-backups" - logical_backup_s3_secret_access_key : "minio123" - logical_backup_s3_sse: "" - logical_backup_schedule: "*/2 * * * *" - configKubernetes: - pod_environment_configmap: "postgres-operator/postgres-pod-config" - configConnectionPooler: - # connection_pooler_image: "registry1.dso.mil/.../pgbouncer:master-18" - connection_pooler_default_cpu_request: "100m" - connection_pooler_default_cpu_limit: "500m" - connection_pooler_default_memory_request: "100Mi" - connection_pooler_default_memory_limit: "100Mi" - resources: - requests: - cpu: "100m" - memory: "250Mi" - limits: - cpu: "500m" - memory: "500Mi" - securityContext: - runAsUser: 1000 - runAsNonRoot: true - readOnlyRootFilesystem: true - allowPrivilegeEscalation: false - podServiceAccount: - name: "zalando-postgres-operator" ---- apiVersion: v1 kind: ConfigMap metadata: @@ -72,11 +13,3 @@ data: WALG_DISABLE_S3_SSE: "true" USE_WALG_RESTORE: "false" AWS_S3_FORCE_PATH_STYLE: "true" ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: zalando-postgres-operator - namespace: postgres-operator -imagePullSecrets: - - name: private-registry \ No newline at end of file diff --git a/examples/postgres-operator/values/minio-instance.yaml b/examples/postgres-operator/values/minio-instance.yaml new file mode 100644 index 0000000000..9b924768e4 --- /dev/null +++ b/examples/postgres-operator/values/minio-instance.yaml @@ -0,0 +1,39 @@ +hostname: minio.localhost +tenants: + pools: + ## Servers specifies the number of MinIO Tenant Pods / Servers in this pool. + ## For standalone mode, supply 1. For distributed mode, supply 4 or more. + ## Note that the operator does not support upgrading from standalone to distributed mode. + - servers: 1 + ## volumesPerServer specifies the number of volumes attached per MinIO Tenant Pod / Server. + volumesPerServer: 4 + ## size specifies the capacity per volume + size: 1Gi + ## storageClass specifies the storage class name to be used for this pool + storageClassName: "###ZARF_STORAGE_CLASS###" + ## Used to specify a toleration for a pod + tolerations: {} + ## nodeSelector parameters for MinIO Pods. It specifies a map of key-value pairs. For the pod to be + ## eligible to run on a node, the node must have each of the + ## indicated key-value pairs as labels. + ## Read more here: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + nodeSelector: {} + ## Affinity settings for MinIO pods. Read more about affinity + ## here: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity. + affinity: {} + ## Configure resource requests and limits for MinIO containers + resources: + requests: + cpu: "250m" + memory: "1Gi" + limits: + cpu: "500m" + memory: "1Gi" + ## Configure security context + ## BB Note: Defaults for Ironbank image are 1001 for user, group, and fsGroup + securityContext: + runAsUser: 1001 + runAsGroup: 1001 + fsGroup: 1001 +console: + enabled: true diff --git a/examples/postgres-operator/values/minio-operator.yaml b/examples/postgres-operator/values/minio-operator.yaml new file mode 100644 index 0000000000..d4824693e9 --- /dev/null +++ b/examples/postgres-operator/values/minio-operator.yaml @@ -0,0 +1,16 @@ +operator: + image: + repository: registry1.dso.mil/ironbank/opensource/minio/operator + tag: v4.2.3 + resources: + requests: + cpu: 200m + memory: 256Mi + ephemeral-storage: 500Mi + limits: + cpu: 200m + memory: 256Mi +# @todo: need to look at if this is neeeded for zarf with SA injection +# tenants: +# imagePullSecret: +# name: zarf-registry diff --git a/examples/postgres-operator/values/pgadmin.yaml b/examples/postgres-operator/values/pgadmin.yaml new file mode 100644 index 0000000000..d29da8690c --- /dev/null +++ b/examples/postgres-operator/values/pgadmin.yaml @@ -0,0 +1,14 @@ +serviceAccount: + create: true +persistentVolume: + size: 2Gi +resources: + requests: + cpu: "100m" + memory: "256Mi" + limits: + cpu: "500m" + memory: "512Mi" +env: + email: "zarf@example.local" + password: "###ZARF_GIT_AUTH_PUSH###" diff --git a/examples/postgres-operator/values/postgres-operator-ui.yaml b/examples/postgres-operator/values/postgres-operator-ui.yaml new file mode 100644 index 0000000000..847b9c3b22 --- /dev/null +++ b/examples/postgres-operator/values/postgres-operator-ui.yaml @@ -0,0 +1,32 @@ +resources: + requests: + cpu: "100m" + memory: "100Mi" + limits: + cpu: "200m" + memory: "200Mi" +envs: + # IMPORTANT: While operator chart and UI chart are idendependent, this is the interface between + # UI and operator API. Insert the service name of the operator API here! + operatorApiUrl: "http://postgres-operator:8080" + operatorClusterNameLabel: "cluster-name" + resourcesVisible: "False" + targetNamespace: "postgres-operator" + teams: + - "acid" +extraEnvs: + - name: WALE_S3_ENDPOINT + value: "http+path://minio.minio-operator.svc.cluster.local:80" + - name: AWS_ENDPOINT + value: "http://minio.minio-operator.svc.cluster.local" + - name: SPILO_S3_BACKUP_PREFIX + value: "spilo/" + - name: AWS_ACCESS_KEY_ID + value: "minio" + - name: AWS_SECRET_ACCESS_KEY + value: "minio123" + - name: SPILO_S3_BACKUP_BUCKET + value: "postgres-operator-backups" +# We are defining our own Ingress manifest +ingress: + enabled: false diff --git a/examples/postgres-operator/values/postgres-operator.yaml b/examples/postgres-operator/values/postgres-operator.yaml new file mode 100644 index 0000000000..56c893e1a6 --- /dev/null +++ b/examples/postgres-operator/values/postgres-operator.yaml @@ -0,0 +1,39 @@ +# configGeneral: + # docker_image: registry1.dso.mil/.../spilo-13:2.1-p1 +configPostgresPodResources: + default_cpu_request: "100m" + default_memory_request: "100Mi" + default_cpu_limit: "500m" + default_memory_limit: "500Mi" + min_cpu_limit: "250m" + min_memory_limit: "250Mi" +configAwsOrGcp: + wal_s3_bucket: "postgres-operator-backups" +configLogicalBackup: + # logical_backup_docker_image: "registry1.dso.mil/.../logical-backup:v1.7.0" + logical_backup_s3_endpoint: "http://minio.minio-operator.svc.cluster.local" + logical_backup_s3_access_key_id: "minio" + logical_backup_s3_bucket: "postgres-operator-backups" + logical_backup_s3_secret_access_key : "minio123" + logical_backup_s3_sse: "" + logical_backup_schedule: "*/2 * * * *" +configKubernetes: + pod_environment_configmap: "postgres-operator/postgres-pod-config" +configConnectionPooler: + # connection_pooler_image: "registry1.dso.mil/.../pgbouncer:master-18" + connection_pooler_default_cpu_request: "100m" + connection_pooler_default_cpu_limit: "500m" + connection_pooler_default_memory_request: "100Mi" + connection_pooler_default_memory_limit: "100Mi" +resources: + requests: + cpu: "100m" + memory: "250Mi" + limits: + cpu: "500m" + memory: "500Mi" +securityContext: + runAsUser: 1000 + runAsNonRoot: true + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false diff --git a/examples/postgres-operator/zarf.yaml b/examples/postgres-operator/zarf.yaml index 7a4e5b519a..2a2ee59633 100644 --- a/examples/postgres-operator/zarf.yaml +++ b/examples/postgres-operator/zarf.yaml @@ -2,35 +2,57 @@ kind: ZarfPackageConfig metadata: name: postgres-operator-demo description: "Demo of prod-like Postgres database(s) on an edge cluster" -# uncompressed: true + # Big Bang / Iron Bank are only amd64 + architecture: amd64 components: - name: baseline required: true - manifests: manifests - - scripts: - retry: true - after: - - "kubectl patch serviceaccount default -p '{\"imagePullSecrets\": [{\"name\": \"private-registry\"}]}' -n postgres-operator" + # Big Bang charts expect this + secretName: "private-registry" + manifests: + - name: postgres-example-config + files: + - manifests/patch-svc-accounts.yaml + - manifests/minio-instance-zarf-connect.yaml + - manifests/pgadmin-zarf-connect.yaml + - manifests/postgres-cluster.yaml + - manifests/postgres-operator.yaml + - manifests/postgres-operator-ui-zarf-connect.yaml charts: - name: postgres-operator url: https://opensource.zalando.com/postgres-operator/charts/postgres-operator version: 1.7.0 + namespace: postgres-operator + valuesFiles: + - values/postgres-operator.yaml - name: postgres-operator-ui url: https://opensource.zalando.com/postgres-operator/charts/postgres-operator-ui version: 1.7.0 + namespace: postgres-operator + valuesFiles: + - values/postgres-operator-ui.yaml - name: pgadmin4 url: https://helm.runix.net version: 1.7.2 + namespace: postgres-operator + valuesFiles: + - values/pgadmin.yaml - name: minio-operator url: https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio-operator.git version: 4.2.3-bb.1 + namespace: minio-operator + gitPath: chart + valuesFiles: + - values/minio-operator.yaml - name: minio-instance url: https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio.git version: 4.2.3-bb.1 - + namespace: minio-operator + gitPath: chart + valuesFiles: + - values/minio-instance.yaml images: - registry.opensource.zalan.do/acid/postgres-operator:v1.7.0 diff --git a/examples/single-big-bang-package/README.md b/examples/single-big-bang-package/README.md index 52cc98da10..31f6a90b59 100644 --- a/examples/single-big-bang-package/README.md +++ b/examples/single-big-bang-package/README.md @@ -1,7 +1,133 @@ -## Zarf Big Bang Single Package Example +# Zarf Single Big Bang Package Example -This example demonstrates using Zarf in a very low-resources/singlue-use environment. In this mode there is no gitops service and Zarf is simply a standard means of wrapping airgap concerns for K3s. This example deploys a basic K3s cluster using Traefik 2 and configures TLS / airgap concerns to deploy a single BB Package. +This example uses Zarf to deploy a single [Big Bang](https://p1.dso.mil/#/products/big-bang/) Package into a KinD cluster. -### Steps to use: -1. Create a Zarf cluster as outlined in the main [README](../../README.md#2-create-the-zarf-cluster) -2. Follow [step 3](../../README.md#3-add-resources-to-the-zarf-cluster) using this config in this folder +## The Flow + +Here's what you'll do in this example: + +1. [Get ready](#get-ready) + +1. [Create a cluster](#create-a-cluster) + +1. [Package it](#package-it) + +1. [Deploy it](#deploy-it) + +1. [Try it](#try-it) + +1. [Cleanup](#cleanup) + +  + +## Get ready + +Before the magic can happen you have to do a few things: + +1. Install [Docker](https://docs.docker.com/get-docker/). Other container engines will likely work as well but aren't actively tested by the Zarf team. + +2. Install [KinD](https://github.com/kubernetes-sigs/kind). Other Kubernetes distros will work as well, but we'll be using KinD for this example since it is easy and tested frequently and thoroughly. + +3. Clone the Zarf project — for the example configuration files. + +4. Download a Zarf release — you need a binary _**and**_ an init package, [here](../../docs/workstation.md#just-gimmie-zarf). + +  + +## Create a cluster + +You can't run software without _somewhere to run it_, so the first thing to do is create a local Kubernetes cluster that Zarf can deploy to. In this example we'll be using KinD to create a lightweight, local K8s cluster running in Docker. + +Kick that off by running this command: + +```sh +kind create cluster +``` + +This will result in a single-node Kubernetes cluster called `kind-kind` on your local machine running in Docker. Your KUBECONFIG should be automatically configured to talk to the new cluster. + +```sh +cd +zarf init +``` + +Follow the prompts, answering "no" to each of the optional components, since we don't need them for this deployment. + +Congratulations! Your machine is now running a single-node Kubernetes cluster powered by Zarf! + +> _**Note**_ +> +> Zarf supports non-interactive installs too! Give `zarf init --confirm --components logging` a try next time. + +**Troubleshooting:** + +> _**ERROR: Unable to find the package on the local system, expected package at zarf-init.tar.zst**_ +> +> The zarf binary needs an init package to know how to setup your cluster! So, if `zarf init` returns an error like this: +> +> ```sh +> ERROR: Unable to find the package on the local system, expected package at zarf-init.tar.zst +> ``` +> +> It's likely you've either forgotten to download `zarf-init.tar.zst` (as part of [getting ready](#get-ready)) _**OR**_ you are _not_ running `zarf init` from the directory the init package is sitting in. + +> _**ERROR: failed to create cluster: node(s) already exist for a cluster with the name "kind"**_ +> +> You already have a KinD cluster running. Either just move on to use the current cluster, or run `kind delete cluster`, then `kind create cluster`. + +> _**Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?**_ +> +> Docker isn't running or you're otherwise not able to talk to it. Check your Docker installation, then try again. + +  + +## Package it + +Zarf is (at heart) a tool for making it easy to get software from _where you have it_ to _**where you need it**_—specifically, across an airgap. Since moving bits is so core to Zarf the idea of a "ready-to-move group of software" has a specific name—the _package_. + +All of the software a Zarf cluster runs is installed via package—for many reasons like versioning, auditability, etc—which means that if you want to run a in your cluster you're going to have to build a package for it. + +Luckily, this is very easy to do—package contents are defined by simple, declarative yaml files and _we've already made one for you_. To build this package you simply: + +```sh +cd /examples/single-big-bang-package # directory with zarf.yaml, and +zarf package create --confirm # make the package +``` + +Watch the terminal scroll for a while. Once things are downloaded & zipped up and you'll see a file ending in `.tar.zst` drop. _That's_ your package. + +  + +## Deploy it + +It's time to feed the package you built into your cluster. + +Since you're running a Zarf cluster directly on your local machine—where this package & `zarf` binary _already are_—deploying the package is very simple: + +```sh +zarf package deploy zarf-package-big-bang-single-package-demo.tar.zst --confirm +``` + +In a couple seconds the cluster will have loaded your package. + +  + +## Use it + +Run `./zarf connect twistlock` to be taken to the twistlock consule in your browser. + +  + +## Cleanup + +Once you've had your fun it's time to clean up. + +In this case, since the Zarf cluster was installed specifically (and _only_) to serve this example, clean up is really easy—you just tear down the entire cluster: + +```sh +kind delete cluster +``` + +It only takes a couple moments for the _entire cluster_ to disappear—long-running system services and all—leaving your machine ready for the next adventure. + +  diff --git a/examples/single-big-bang-package/manifests/image-pull-secret.yaml b/examples/single-big-bang-package/manifests/image-pull-secret.yaml deleted file mode 100644 index 2b723c3f32..0000000000 --- a/examples/single-big-bang-package/manifests/image-pull-secret.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: twistlock -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } diff --git a/examples/single-big-bang-package/manifests/twistlock.yaml b/examples/single-big-bang-package/manifests/twistlock.yaml deleted file mode 100644 index f75ac7a3c5..0000000000 --- a/examples/single-big-bang-package/manifests/twistlock.yaml +++ /dev/null @@ -1,37 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: twistlock ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: twistlock - namespace: twistlock -spec: - chart: https://%{KUBERNETES_API}%/static/charts/twistlock-0.0.6-bb.1.tgz - targetNamespace: twistlock - valuesContent: |- - imagePullSecrets: - - name: private-registry ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: twistlock-ingress - namespace: twistlock - annotations: - kubernetes.io/ingress.class: "traefik" - traefik.ingress.kubernetes.io/router.middlewares: kube-system-ssl-redirect@kubernetescrd -spec: - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: twistlock-console - port: - number: 8081 diff --git a/examples/single-big-bang-package/twistlock-zarf-connect.yaml b/examples/single-big-bang-package/twistlock-zarf-connect.yaml new file mode 100644 index 0000000000..f0ce5db67a --- /dev/null +++ b/examples/single-big-bang-package/twistlock-zarf-connect.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + zarf.dev/connect-description: "Connect to the Twistlock Console web interface" + labels: + # Enables "zarf connect twistlock" + zarf.dev/connect-name: twistlock + name: twistlock-ui-connect + namespace: twistlock +spec: + type: ClusterIP + ports: + - name: mgmt-http + port: 8081 + protocol: TCP + targetPort: 8081 + selector: + name: twistlock-console diff --git a/examples/single-big-bang-package/zarf.yaml b/examples/single-big-bang-package/zarf.yaml index 172b416278..1399265c60 100644 --- a/examples/single-big-bang-package/zarf.yaml +++ b/examples/single-big-bang-package/zarf.yaml @@ -2,17 +2,22 @@ kind: ZarfPackageConfig metadata: name: big-bang-single-package-demo description: "Demo Zarf appliance mode with a single DoD Platform One Big Bang package" + # Big Bang / Iron Bank are only amd64 + architecture: amd64 components: - name: baseline required: true - manifests: manifests - + manifests: + - name: twistlock-zarf-connect + files: + - "twistlock-zarf-connect.yaml" charts: - name: twistlock url: https://repo1.dso.mil/platform-one/big-bang/apps/security-tools/twistlock.git version: 0.0.6-bb.1 - + namespace: twistlock + gitPath: chart # https://umbrella-bigbang-releases.s3-us-gov-west-1.amazonaws.com/umbrella/1.14.0/images.txt images: - registry1.dso.mil/ironbank/twistlock/defender/defender:20.12.531 diff --git a/examples/software-factory/Makefile b/examples/software-factory/Makefile deleted file mode 100755 index 127b3355af..0000000000 --- a/examples/software-factory/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -# Figure out which Zarf binary we should use based on the operating system we are on -ZARF_BIN := ../sync/zarf -UNAME_S := $(shell uname -s) -UNAME_P := $(shell uname -p) -ifneq ($(UNAME_S),Linux) - ifeq ($(UNAME_S),Darwin) - ZARF_BIN := $(addsuffix -mac,$(ZARF_BIN)) - endif - ifeq ($(UNAME_P),i386) - ZARF_BIN := $(addsuffix -intel,$(ZARF_BIN)) - endif - ifeq ($(UNAME_P),arm64) - ZARF_BIN := $(addsuffix -apple,$(ZARF_BIN)) - endif -endif - -.DEFAULT_GOAL := help - - -.PHONY: help -help: ## Show a list of all targets - @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ - | sed -n 's/^\(.*\): \(.*\)##\(.*\)/\1:\3/p' \ - | column -t -s ":" - -.PHONY: all -all: clean fetch-release package-example-software-factory vm-init ## Download zarf, build all packages and launch a basic VM with the assets - -.PHONY: all-dev -all-dev: clean build-release package-example-software-factory vm-init ## Same as target 'all', but build the binaries using the current codebase rather than downloading the latest version from the internet - -.PHONY: clean -clean: ## Clean the sync dir - @cd .. && $(MAKE) clean - -.PHONY: fetch-release -fetch-release: ## Grab the latest release as an alternative to needing to build the binaries - @cd .. && $(MAKE) fetch-release - -.PHONY: build-release -build-release: ## Build the binaries as an alternative to downloading the latest release - @cd .. && $(MAKE) build-release - -.PHONY: vm-init -vm-init: vm-destroy ## Stripped-down vagrant box to reduce friction for basic user testing. Note the need to perform disk resizing for some examples - @cd .. && $(MAKE) vm-init - -.PHONY: vm-destroy -vm-destroy: ## Cleanup plz - @cd .. && $(MAKE) vm-destroy - -.PHONY: package-example-software-factory -package-example-software-factory: ## Create the software factory deploy package - @kustomize build template/bigbang > manifests/bigbang/bigbang-generated.yaml && kustomize build template/flux > manifests/flux/flux-generated.yaml && $(ZARF_BIN) package create --confirm && mv zarf-package-* ../sync/ - -.PHONY: ssh -ssh: ## SSH into the Vagrant VM - @cd .. && vagrant ssh diff --git a/examples/software-factory/README.md b/examples/software-factory/README.md deleted file mode 100644 index 489cca130b..0000000000 --- a/examples/software-factory/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# Example: Software Factory - -This example deploys the components of a software factory with the following services, all running on top of Big Bang Core: - -- SonarQube* -- GitLab* -- GitLab Runner* -- Minio Operator* -- Mattermost Operator* -- Mattermost* -- Nexus* -- Keycloak* -- Jira -- Confluence -- Jenkins - -**Deployed using Big Bang Umbrella* - -This package is huge. We recommend not trying to run it on a developer laptop without disabling lots of stuff first. - -> Note: Right now the intention is to show that all of these services can be deployed easily using a single Zarf package. They are not configured (yet). You can't take this demo and deploy it expecting to have a fully operational software factory at the push of a button, though that is the end goal. There's a lot of work to do between what is here now and that end goal, some of which might just not make very much sense in the context of a demo/example. - -## Prerequisites - -- Logged into registry1.dso.mil -- `make` -- `kustomize` -- `sha256sum` -- TONS of CPU and RAM. Our testing shows the EC2 instance type m6i.8xlarge works pretty well at about $1.50/hour, which can be reduced further if you do a spot instance. -- [Vagrant](https://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/), only if you are going to use a Vagrant VM, which is incompatible when using an EC2 instance. - -Note: Vagrant and VirtualBox aren't required for Zarf to function, but this example's Makefile uses them to create a VM which everything will run in. In production you'll likely just run Zarf on the machine itself. - -## Instructions - -1. `cd examples/software-factory` -1. Run one of these two commands: - - `make all` - Download the latest version of Zarf, build the deploy package, and start a VM with Vagrant - - `make all-dev` - Build Zarf locally, build the deploy package, and start a VM with Vagrant. Requires Golang. - - > Note: If you are in an EC2 instance you should skip the `vm-init` make target, so run `make clean fetch-release package-example-software-factory && cd ../sync && sudo su` instead, then move on to the next step. -1. Run: `./zarf init --confirm --components management,gitops-service --host 127.0.0.1` - Initialize Zarf, telling it to install the management component and gitops service and skip logging component (since BB has logging already) and tells Zarf to use `127.0.0.1` as the cluster's address. If you want to use interactive mode instead just run `./zarf init`. -1. Wait a bit, run `k9s` to see pods come up. Don't move on until everything is running -1. Run: `./zarf package deploy zarf-package-software-factory-demo.tar.zst --confirm` - Deploy the software factory package. If you want interactive mode instead just run `./zarf package deploy`, it will give you a picker to choose the package. -1. Wait several minutes. Run `k9s` to watch progress -1. :warning: `kubectl delete -n istio-system envoyfilter/misdirected-request` (due to [this bug](https://repo1.dso.mil/platform-one/big-bang/bigbang/-/issues/802)) -1. Use a browser to visit the various services, available at https://*.bigbang.dev:9443 -1. When you're done, run `exit` to leave the VM then `make vm-destroy` to bring everything down - -## Notes - -- If you are not running in a Vagrant box created with the Vagrantfile in ./examples you will have to run `sysctl -w vm.max_map_count=262144` to get ElasticSearch to start correctly. -- If you want to turn off certain services to help the package run on smaller machines go into `template/bigbang/values.yaml` and change `enabled: true` to `enabled: false` for each service you want to disable. You can disable the Atlassian stack or Jenkins from `zarf.yaml`. Change `required: true` to `required:false` then press `N` when asked whether you want to deploy them. - -## Services - -| URL | Username | Password | Notes | -| ----------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------- | -| [AlertManager](https://alertmanager.bigbang.dev:9443) | n/a | n/a | Unauthenticated | -| [Grafana](https://grafana.bigbang.dev:9443) | `admin` | `prom-operator` | | -| [Kiali](https://kiali.bigbang.dev:9443) | n/a | `kubectl get secret -n kiali -o=json \| jq -r '.items[] \| select(.metadata.annotations."kubernetes.io/service-account.name"=="kiali-service-account") \| .data.token' \| base64 -d; echo` | | -| [Kibana](https://kibana.bigbang.dev:9443) | `elastic` | `kubectl get secret -n logging logging-ek-es-elastic-user -o=jsonpath='{.data.elastic}' \| base64 -d; echo` | | -| [Prometheus](https://prometheus.bigbang.dev:9443) | n/a | n/a | Unauthenticated | -| [Jaeger](https://tracing.bigbang.dev:9443) | n/a | n/a | Unauthenticated | -| [Twistlock](https://twistlock.bigbang.dev:9443) | n/a | n/a | | -| [Jira](https://jira.bigbang.dev:9443) | n/a | n/a | | -| [Confluence](https://confluence.bigbang.dev:9443) | n/a | n/a | | -| [GitLab](https://gitlab.bigbang.dev:9443) | n/a | n/a | | -| [Nexus](https://nexus.bigbang.dev:9443) | n/a | n/a | | -| [Mattermost](https://chat.bigbang.dev:9443) | n/a | n/a | | -| [Sonarqube](https://sonarqube.bigbang.dev:9443) | n/a | n/a | | -| [Jenkins](https://jenkins.bigbang.dev:9443) | `admin` | `admin` | | diff --git a/examples/software-factory/manifests/.gitignore b/examples/software-factory/manifests/.gitignore deleted file mode 100644 index 0c6553f63e..0000000000 --- a/examples/software-factory/manifests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*-generated.yaml diff --git a/examples/software-factory/manifests/atlassian/atlassian-manifests.yaml b/examples/software-factory/manifests/atlassian/atlassian-manifests.yaml deleted file mode 100644 index 577290ead9..0000000000 --- a/examples/software-factory/manifests/atlassian/atlassian-manifests.yaml +++ /dev/null @@ -1,117 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: helm-install-atlassian - labels: - istio-injection: "disabled" ---- -apiVersion: v1 -kind: Namespace -metadata: - name: jira - labels: - istio-injection: "enabled" ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: jira - namespace: helm-install-atlassian -spec: - chart: https://%{KUBERNETES_API}%/static/charts/jira-0.1.0-bb.7.tgz - targetNamespace: jira - valuesContent: |- - imagePullSecrets: - - name: private-registry - ingress: - nginx: false - istio: - enabled: true - gateways: - - istio-system/public - ---- -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: jira -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } ---- -apiVersion: v1 -kind: Namespace -metadata: - name: confluence - labels: - istio-injection: "enabled" ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: confluence - namespace: helm-install-atlassian -spec: - chart: https://%{KUBERNETES_API}%/static/charts/confluence-0.1.0-bb.9.tgz - targetNamespace: confluence - valuesContent: |- - image: - repository: registry1.dso.mil/ironbank/atlassian/confluence-data-center/confluence-node:7.13.0 - tag: "7.13.0" - imagePullSecrets: - - name: private-registry - ingress: - nginx: false - istio: - enabled: true - gateways: - - istio-system/public - ---- -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: confluence -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } diff --git a/examples/software-factory/manifests/bigbang/bigbang-manifests.yaml b/examples/software-factory/manifests/bigbang/bigbang-manifests.yaml deleted file mode 100644 index 1004902169..0000000000 --- a/examples/software-factory/manifests/bigbang/bigbang-manifests.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: zarf-git-secret - namespace: bigbang -stringData: - username: "zarf-git-user" - password: "###ZARF_SECRET###" ---- -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: gitlab -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } diff --git a/examples/software-factory/manifests/flux/flux-manifests.yaml b/examples/software-factory/manifests/flux/flux-manifests.yaml deleted file mode 100644 index 5c36c6c3de..0000000000 --- a/examples/software-factory/manifests/flux/flux-manifests.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: flux-system -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } diff --git a/examples/software-factory/manifests/jenkins/jenkins-manifests.yaml b/examples/software-factory/manifests/jenkins/jenkins-manifests.yaml deleted file mode 100644 index e1276d9859..0000000000 --- a/examples/software-factory/manifests/jenkins/jenkins-manifests.yaml +++ /dev/null @@ -1,111 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: helm-install-jenkins -labels: - istio-injection: "disabled" ---- -apiVersion: v1 -kind: Namespace -metadata: - name: jenkins -labels: - istio-injection: "enabled" ---- -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: jenkins -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: jenkins - namespace: helm-install-jenkins -spec: - chart: https://%{KUBERNETES_API}%/static/charts/jenkins-3.9.4.tgz - targetNamespace: jenkins - valuesContent: |- - controller: - image: "jenkins/jenkins" - tag: "2.319.1-jdk11" - imagePullSecretName: "private-registry" - adminUser: "admin" - adminPassword: "admin" - resources: - requests: - cpu: "50m" - memory: "256Mi" - limits: - cpu: "2000m" - memory: "4096Mi" - initContainerResources: - requests: - cpu: "50m" - memory: "256Mi" - limits: - cpu: "2000m" - memory: "4096Mi" - installPlugins: - - kubernetes:1.31.1 - - workflow-aggregator:2.6 - - git:4.10.1 - - configuration-as-code:1.55 - jenkinsUrlProtocol: "https" - jenkinsUrl: "jenkins.bigbang.dev:9443" - agent: - enabled: true - image: "jenkins/inbound-agent" - tag: "4.11-1" - imagePullSecretName: "private-registry" - resources: - requests: - cpu: "512m" - memory: "512Mi" - limits: - cpu: "512m" - memory: "512Mi" - alwaysPullImage: true - persistence: - enabled: true - storageClass: "local-path" ---- -apiVersion: networking.istio.io/v1beta1 -kind: VirtualService -metadata: - name: jenkins - namespace: jenkins -spec: - gateways: - - istio-system/public - hosts: - - jenkins.bigbang.dev - http: - - route: - - destination: - host: jenkins.jenkins.svc.cluster.local - port: - number: 8080 diff --git a/examples/software-factory/template/bigbang/kustomization.yaml b/examples/software-factory/template/bigbang/kustomization.yaml deleted file mode 100644 index 318682a4a7..0000000000 --- a/examples/software-factory/template/bigbang/kustomization.yaml +++ /dev/null @@ -1,21 +0,0 @@ -bases: - - git::https://repo1.dso.mil/platform-one/big-bang/bigbang.git/base?ref=1.17.0 - -configMapGenerator: - - name: common - namespace: bigbang - behavior: merge - files: - - values.yaml - -patchesStrategicMerge: -- |- - apiVersion: source.toolkit.fluxcd.io/v1beta1 - kind: GitRepository - metadata: - name: bigbang - namespace: bigbang - spec: - url: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__bigbang.git - secretRef: - name: zarf-git-secret diff --git a/examples/software-factory/template/bigbang/values.yaml b/examples/software-factory/template/bigbang/values.yaml deleted file mode 100644 index bba477ca6d..0000000000 --- a/examples/software-factory/template/bigbang/values.yaml +++ /dev/null @@ -1,805 +0,0 @@ -domain: bigbang.dev - -registryCredentials: - - registry: "registry1.dso.mil" - username: "zarf-git-user" - password: "###ZARF_SECRET###" - - registry: "docker.io" - username: "zarf-git-user" - password: "###ZARF_SECRET###" - - registry: "registry.dso.mil" - username: "zarf-git-user" - password: "###ZARF_SECRET###" - -git: - existingSecret: "zarf-git-secret" - -flux: - interval: 1m - rollback: - cleanupOnFail: false - -networkPolicies: - enabled: false - # When in prod use a real CIDR. Don't do this, it isn't secure. This is done here since it is a demo and the CIDR changes based on which Linux distro you are running on. - controlPlaneCidr: "0.0.0.0/0" - nodeCidr: "0.0.0.0/0" - -istio: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__istio-controlplane.git - ingressGateways: - public-ingressgateway: - type: "LoadBalancer" - kubernetesResourceSpec: - resources: - requests: - cpu: "100m" - memory: "512Mi" - limits: - cpu: "500m" - memory: "512Mi" - service: - ports: - - name: status-port - port: 15021 - protocol: TCP - targetPort: 15021 - - name: http2 - port: 9080 - protocol: TCP - targetPort: 8080 - - name: https - port: 9443 - protocol: TCP - targetPort: 8443 - - name: tls - port: 15443 - protocol: TCP - targetPort: 15443 - passthrough-ingressgateway: - type: "LoadBalancer" # or "LoadBalancer" - kubernetesResourceSpec: - resources: - requests: - cpu: "100m" - memory: "512Mi" - limits: - cpu: "500m" - memory: "512Mi" - service: - ports: - - name: status-port - port: 15022 - protocol: TCP - targetPort: 15021 - - name: http2 - port: 19080 - protocol: TCP - targetPort: 8080 - - name: https - port: 19443 - protocol: TCP - targetPort: 8443 - - name: tls - port: 15444 - protocol: TCP - targetPort: 15443 - - gateways: - public: - tls: - key: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbaLWaC86eG74Z - D5JxLJ0X4DpOTZgGeP3oY+oS5S1pE+nZq30LrC6YMQeBLSvJDWpBtvV5x9F88gMz - yhU94HgrWH26LBUQIBti+ip6IbS0sAKc6bicw6NBtR2F4BnLGw+mrUniVT8WNrRL - C1NkN5shexmTE6XAY9Ak6UpApHVmTiB8xz6hypr4JwqnqQfxDO0+AfaGSHheKo5h - xTSgUYULhyA9UaImHU+S/SekwGLRLX1KfcTpnz1+TZiQqShG9vqUB4dAge+imwAs - ZTCnI9H3tmz6jWekXQYRUraJUwjEaqqLoSQT5VQmEl518ueeRKKNB/8mi1pylWqN - UjedV4A5AgMBAAECggEBAM56xORaljBO9WAKOotNK+1rNBO6jAYTWQeY95CeolSP - y/PvobcZa6QICAL16o3DlSqQroTTmf7WllLnq4PWueA43+ETWSMaxAsqWE0laTTd - qyfV/8lvhzTv5/+z/TIZnmoCDFT2Wm9iPdudpfXbKp+ghFnYFJVwmVITRbB91InX - 38LaEvLWFnJ3/DPYursaXerwwrm50d0PCdpa/ceqBCVHlpT3Zc0lT0rYpDVtc9BG - 3gjbvKwhVUQBDfD3FGEobxhbc5eEH6JEf0PUWKnsU5F0qRKjQnfM19XKbczP+9gY - 71BDL1sALSZxxJXW865+7GeXKCtxObkcCwYbf8UrS30CgYEA+HSH4ZpuHZ8IKIbs - vFaAjsEMkRfZPao8b/g4/JCg4TuOpAdFZUTSPWmdUq3i/J8o9b+e8/bznn9HLHIT - qyreSyiRUQRtcniSL1ZUHSzzW9QefYKzPghGYHXQLIBAWt50PDaMfPQ6Sj1NaEPH - h3hq4YNYNMQP/QVmfFdiT4xVA6cCgYEA4hJgSc17hh/u84uYAKhg2zSlFG5LlYKc - Yb2aFQJhFz2QqGxMeOXyIVDFD6btGcOLtPt4RdsBuCLZZzFBDUlWL7rY9qlL+/+P - ERStyHE9gFBDa0KWfvQxHSXIuxN2mkokktiVfaTisi8SWEKRJYp+B8HCa5lSDBti - eXcGBK3hWR8CgYBJ+aBPmsR4i1ZJgsrP1M2YM4CDXt9uzdYK3JRTFtjf1vTEf+m4 - mkIiyORvrphr8ROn//La3sdwhKLzZ8/VYgEnzZ9eyPuxXpbgA0suGKkoyUJ+ykCG - Er6pj8p4xYLjy2I+X1t7BNiqLBB1H+Ezw7XHCW1k4I+GHWqDUR1TZAwX9wKBgFhy - KAm3wqPuymWuL4HSXlJkflFH9XpA5z22GBowHBwjkfzSofiKvfgayX4eKJTz1Cyy - VZO+4yVPPQ8KThEMqBN0Xn3iLkAg87ATDwpkg1M4E6hbHNX+Y1ir96R5MOWcLELn - SVUmtSpREDRHltHBJR2TyKSgD2F9NUGgN1KNVKSxAoGARyx7VceWlpdmnr+i26UH - B4h6/rL/nY7M2oWgUaj7FeygcfemtO6cV+R1Bl876Q9Dx797hZ4ddGAgxmDFsv8J - f6SSzTJBB6IGxt+1ZcxD4uFXUrOVFv00br/Re14bsXQcMwi9kEJF2idbR5E7O2qc - qbLlPssjuZS5pDnRa05bEIQ= - -----END PRIVATE KEY----- - cert: | - -----BEGIN CERTIFICATE----- - MIIFHzCCBAegAwIBAgISA9KlIFfDVyxZ1/qZXl4HMuIOMA0GCSqGSIb3DQEBCwUA - MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD - EwJSMzAeFw0yMTA5MjcxNDU1MDdaFw0yMTEyMjYxNDU1MDZaMBgxFjAUBgNVBAMM - DSouYmlnYmFuZy5kZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb - aLWaC86eG74ZD5JxLJ0X4DpOTZgGeP3oY+oS5S1pE+nZq30LrC6YMQeBLSvJDWpB - tvV5x9F88gMzyhU94HgrWH26LBUQIBti+ip6IbS0sAKc6bicw6NBtR2F4BnLGw+m - rUniVT8WNrRLC1NkN5shexmTE6XAY9Ak6UpApHVmTiB8xz6hypr4JwqnqQfxDO0+ - AfaGSHheKo5hxTSgUYULhyA9UaImHU+S/SekwGLRLX1KfcTpnz1+TZiQqShG9vqU - B4dAge+imwAsZTCnI9H3tmz6jWekXQYRUraJUwjEaqqLoSQT5VQmEl518ueeRKKN - B/8mi1pylWqNUjedV4A5AgMBAAGjggJHMIICQzAOBgNVHQ8BAf8EBAMCBaAwHQYD - VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0O - BBYEFLUbMi65bMLlINPzTplLjtCHZfa0MB8GA1UdIwQYMBaAFBQusxe3WFbLrlAJ - QOYfr52LFMLGMFUGCCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDovL3Iz - Lm8ubGVuY3Iub3JnMCIGCCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5vcmcv - MBgGA1UdEQQRMA+CDSouYmlnYmFuZy5kZXYwTAYDVR0gBEUwQzAIBgZngQwBAgEw - NwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5j - cnlwdC5vcmcwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdQBElGUusO7Or8RAB9io - /ijA2uaCvtjLMbU/0zOWtbaBqAAAAXwn948JAAAEAwBGMEQCIBkkdKr6WRtmZYO8 - kuchAYDxGPaCnU9FYU3BZBpsbJvLAiButEYn4AvTFiZMILymyuuqct/eFjIR9MEE - pNotyaD+bQB2AH0+8viP/4hVaCTCwMqeUol5K8UOeAl/LmqXaJl+IvDXAAABfCf3 - kGUAAAQDAEcwRQIhAOOOX0qpI8xjqARUfU4ErGe8icHORlNHHzP/a6b3XE4ZAiBp - fMNh3oihXS1e6EM9Xs8m+9nuCi7rqLNSkCNuwisK7zANBgkqhkiG9w0BAQsFAAOC - AQEABMjkLKKxYyL4ZT6BPuOyqC4hnczDYUmZdCCysLu7psCjrZIAlSRxLIWXdWir - ogi/Vf+wdPKk38NDar0T9+rfAehuvQjQKCzIKVzr+MGauW0Wytwt63EgLIl2znvX - jWEIUwDQkqeFzPMbov8BK8hdLibBSz9nLrT0Zyw9mgRIzslemsi62+AjSNERTCTv - qyhinnBHLd3dGLOAXexwXu7ic2ZwCgnSgcli+MWC30QOh6ePJJqgw6OpwvOC9DAV - fkvGYFXlgYXnhQeLr0/4tzw3koclRWe/qgjAdAjB03yp1e53b+j9NoOfyobo1MFe - nMqEgcgAiA2VuE62Q4HE0Rs5wA== - -----END CERTIFICATE----- - -----BEGIN CERTIFICATE----- - MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw - TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh - cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw - WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg - RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK - AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP - R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx - sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm - NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg - Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG - /kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC - AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB - Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA - FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw - AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw - Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB - gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W - PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl - ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz - CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm - lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 - avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 - yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O - yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids - hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ - HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv - MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX - nLRbwHOoq7hHwg== - -----END CERTIFICATE----- - -----BEGIN CERTIFICATE----- - MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/ - MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT - DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow - TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh - cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB - AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC - ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL - wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D - LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK - 4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5 - bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y - sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ - Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4 - FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc - SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql - PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND - TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw - SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1 - c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx - +tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB - ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu - b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E - U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu - MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC - 5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW - 9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG - WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O - he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC - Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5 - -----END CERTIFICATE----- - passthrough: - ingressGateway: "passthrough-ingressgateway" - hosts: - - "*.{{ .Values.domain }}" - tls: - mode: "PASSTHROUGH" - - values: - istiod: - hpaSpec: - maxReplicas: 1 - minReplicas: 1 - resources: - requests: - cpu: "100m" - memory: "1Gi" - limits: - cpu: "500m" - memory: "1Gi" - kiali: - dashboard: - auth: - strategy: "anonymous" - -istiooperator: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__istio-operator.git - values: - operator: - resources: - requests: - cpu: "100m" - memory: "256Mi" - limits: - cpu: "500m" - memory: "256Mi" - -jaeger: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__jaeger.git - values: - resources: - requests: - cpu: "100m" - memory: "128Mi" - limits: - cpu: "500m" - memory: "128Mi" - jaeger: - spec: - allInOne: - resources: - requests: - cpu: "100m" - memory: "128Mi" - limits: - cpu: "500m" - memory: "128Mi" - collector: - resources: - requests: - cpu: "100m" - memory: "128Mi" - limits: - cpu: "500m" - memory: "128Mi" - ingester: - # TODO: Remove this once the upstream bug is fixed (https://repo1.dso.mil/platform-one/big-bang/apps/core/jaeger/-/issues/15) - image: registry1.dso.mil/ironbank/opensource/jaegertracing/jaeger-ingester:1.24.0 - -kiali: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__kiali.git - values: - resources: - requests: - cpu: "100m" - memory: "256Mi" - limits: - cpu: "500m" - memory: "256Mi" - cr: - spec: - deployment: - resources: - requests: - cpu: "100m" - memory: "368Mi" - limits: - cpu: "500m" - memory: "368Mi" - -clusterAuditor: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__cluster-auditor.git - values: - resources: - requests: - cpu: "100m" - memory: "512Mi" - limits: - cpu: "500m" - memory: "512Mi" - -gatekeeper: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__policy.git - values: - replicas: 1 - controllerManager: - resources: - requests: - cpu: "175m" - memory: "512Mi" - limits: - cpu: "1" - memory: "2Gi" - audit: - resources: - requests: - cpu: "200m" - memory: "768Mi" - limits: - cpu: "1.2" - memory: "2Gi" - violations: - allowedDockerRegistries: - parameters: - excludedResources: - # K3s kube-system stuff, better than excluding the whole namespace - - "kube-system/coredns-.*" - - "kube-system/local-path-provisioner-.*" - - "kube-system/metrics-server-.*" - - "kube-system/svclb-.*" - - "kube-system/traefik-.*" - # K3s needs these due to how it creates services of type "LoadBalancer" - - "istio-system/lb-port-.*" - - "istio-system/svclb-.*" - # K3s needs this if you are doing K3s-specific "HelmRelease"-type CRDs - - ".*/helm-install-.*" - - ".*/helm" - # TODO: Get Gitea in Iron Bank - - "git/stuart-gitea-.*" - - "git/gitea" - - "git/init" - hostNetworking: - parameters: - excludedResources: - # K3s needs these due to how it creates services of type "LoadBalancer" - - "istio-system/svclb-.*" - - "istio-system/lb-port-.*" - httpsOnly: - parameters: - excludedResources: - # TODO: Fix these ingresses so they don't need to be excluded - - "git/git-ingress" - - "registry/registry-ingress" - -logging: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__elasticsearch-kibana.git - values: - elasticsearch: - master: - count: 1 - persistence: - size: "5Gi" - resources: - requests: - cpu: "100m" - memory: "3Gi" - limits: - cpu: "500m" - memory: "3Gi" - data: - count: 1 - persistence: - size: 5Gi - resources: - requests: - cpu: "100m" - memory: "3Gi" - limits: - cpu: "500m" - memory: "3Gi" - kibana: - count: 1 - resources: - requests: - memory: "1Gi" - cpu: "100m" - limits: - memory: "1Gi" - cpu: "500m" - -eckoperator: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__eck-operator.git - -fluentbit: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__fluentbit.git - values: - securityContext: - privileged: true - resources: - requests: - cpu: "100m" - memory: "128Mi" - limits: - cpu: "500m" - memory: "128Mi" - -monitoring: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__core__monitoring.git - values: - alertmanager: - alertmanagerSpec: - resources: - requests: - cpu: "100m" - memory: "256Mi" - limits: - cpu: "500m" - memory: "256Mi" - prometheusOperator: - resources: - requests: - cpu: "100m" - memory: "512Mi" - limits: - cpu: "500m" - memory: "512Mi" - prometheus: - prometheusSpec: - resources: - requests: - cpu: "100m" - memory: "512Mi" - limits: - cpu: "500m" - memory: "2Gi" - grafana: - sidecar: - resources: - requests: - cpu: "50m" - memory: "50Mi" - limits: - cpu: "500m" - memory: "100Mi" - resources: - requests: - cpu: "100m" - memory: "128Mi" - limits: - cpu: "500m" - memory: "128Mi" - kube-state-metrics: - resources: - requests: - cpu: "10m" - memory: "128Mi" - limits: - cpu: "500m" - memory: "128Mi" - prometheus-node-exporter: - resources: - requests: - cpu: "100m" - memory: "128Mi" - limits: - cpu: "500m" - memory: "128Mi" - -twistlock: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__security-tools__twistlock.git - values: - console: - persistence: - size: 5Gi - resources: - requests: - cpu: "100m" - memory: "1Gi" - limits: - cpu: "500m" - memory: "2Gi" - - -addons: - # Addons for bb-umbrella - sonarqube: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__developer-tools__sonarqube.git - values: - istio: - enabled: true - sonarqube: - gateways: - - istio-system/public - postgresql: - global: - imagePullSecrets: - - "private-registry" - gitlab: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__developer-tools__gitlab.git - values: - global: - istio: - enabled: true - imagePullSecrets: - - "private-registry" - istio: - enabled: true - injection: enabled - gitlab: - gateways: - - istio-system/public - gitlabRunner: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__developer-tools__gitlab-runner.git - minioOperator: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__application-utilities__minio-operator.git - values: - istio: - enabled: true - mattermostoperator: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__collaboration-tools__mattermost-operator.git - tag: "1.16.0-bb.0" - values: - istio: - enabled: true - mattermost: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__collaboration-tools__mattermost.git - tag: 0.2.4-bb.0 - values: - istio: - enabled: true - chat: - gateways: - - istio-system/public - minio: - install: true - bucketCreationImage: "registry1.dso.mil/ironbank/opensource/minio/minio:RELEASE.2021-08-31T05-46-54Z" - nexus: - enabled: true - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__developer-tools__nexus.git - tag: 36.0.0-bb.0 - values: - istio: - enabled: true - nexus: - gateways: - - istio-system/public - keycloak: - enabled: false - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__security-tools__keycloak.git - tag: "11.0.1-bb.6" - ingress: - key: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDbaLWaC86eG74Z - D5JxLJ0X4DpOTZgGeP3oY+oS5S1pE+nZq30LrC6YMQeBLSvJDWpBtvV5x9F88gMz - yhU94HgrWH26LBUQIBti+ip6IbS0sAKc6bicw6NBtR2F4BnLGw+mrUniVT8WNrRL - C1NkN5shexmTE6XAY9Ak6UpApHVmTiB8xz6hypr4JwqnqQfxDO0+AfaGSHheKo5h - xTSgUYULhyA9UaImHU+S/SekwGLRLX1KfcTpnz1+TZiQqShG9vqUB4dAge+imwAs - ZTCnI9H3tmz6jWekXQYRUraJUwjEaqqLoSQT5VQmEl518ueeRKKNB/8mi1pylWqN - UjedV4A5AgMBAAECggEBAM56xORaljBO9WAKOotNK+1rNBO6jAYTWQeY95CeolSP - y/PvobcZa6QICAL16o3DlSqQroTTmf7WllLnq4PWueA43+ETWSMaxAsqWE0laTTd - qyfV/8lvhzTv5/+z/TIZnmoCDFT2Wm9iPdudpfXbKp+ghFnYFJVwmVITRbB91InX - 38LaEvLWFnJ3/DPYursaXerwwrm50d0PCdpa/ceqBCVHlpT3Zc0lT0rYpDVtc9BG - 3gjbvKwhVUQBDfD3FGEobxhbc5eEH6JEf0PUWKnsU5F0qRKjQnfM19XKbczP+9gY - 71BDL1sALSZxxJXW865+7GeXKCtxObkcCwYbf8UrS30CgYEA+HSH4ZpuHZ8IKIbs - vFaAjsEMkRfZPao8b/g4/JCg4TuOpAdFZUTSPWmdUq3i/J8o9b+e8/bznn9HLHIT - qyreSyiRUQRtcniSL1ZUHSzzW9QefYKzPghGYHXQLIBAWt50PDaMfPQ6Sj1NaEPH - h3hq4YNYNMQP/QVmfFdiT4xVA6cCgYEA4hJgSc17hh/u84uYAKhg2zSlFG5LlYKc - Yb2aFQJhFz2QqGxMeOXyIVDFD6btGcOLtPt4RdsBuCLZZzFBDUlWL7rY9qlL+/+P - ERStyHE9gFBDa0KWfvQxHSXIuxN2mkokktiVfaTisi8SWEKRJYp+B8HCa5lSDBti - eXcGBK3hWR8CgYBJ+aBPmsR4i1ZJgsrP1M2YM4CDXt9uzdYK3JRTFtjf1vTEf+m4 - mkIiyORvrphr8ROn//La3sdwhKLzZ8/VYgEnzZ9eyPuxXpbgA0suGKkoyUJ+ykCG - Er6pj8p4xYLjy2I+X1t7BNiqLBB1H+Ezw7XHCW1k4I+GHWqDUR1TZAwX9wKBgFhy - KAm3wqPuymWuL4HSXlJkflFH9XpA5z22GBowHBwjkfzSofiKvfgayX4eKJTz1Cyy - VZO+4yVPPQ8KThEMqBN0Xn3iLkAg87ATDwpkg1M4E6hbHNX+Y1ir96R5MOWcLELn - SVUmtSpREDRHltHBJR2TyKSgD2F9NUGgN1KNVKSxAoGARyx7VceWlpdmnr+i26UH - B4h6/rL/nY7M2oWgUaj7FeygcfemtO6cV+R1Bl876Q9Dx797hZ4ddGAgxmDFsv8J - f6SSzTJBB6IGxt+1ZcxD4uFXUrOVFv00br/Re14bsXQcMwi9kEJF2idbR5E7O2qc - qbLlPssjuZS5pDnRa05bEIQ= - -----END PRIVATE KEY----- - cert: | - -----BEGIN CERTIFICATE----- - MIIFHzCCBAegAwIBAgISA9KlIFfDVyxZ1/qZXl4HMuIOMA0GCSqGSIb3DQEBCwUA - MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD - EwJSMzAeFw0yMTA5MjcxNDU1MDdaFw0yMTEyMjYxNDU1MDZaMBgxFjAUBgNVBAMM - DSouYmlnYmFuZy5kZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb - aLWaC86eG74ZD5JxLJ0X4DpOTZgGeP3oY+oS5S1pE+nZq30LrC6YMQeBLSvJDWpB - tvV5x9F88gMzyhU94HgrWH26LBUQIBti+ip6IbS0sAKc6bicw6NBtR2F4BnLGw+m - rUniVT8WNrRLC1NkN5shexmTE6XAY9Ak6UpApHVmTiB8xz6hypr4JwqnqQfxDO0+ - AfaGSHheKo5hxTSgUYULhyA9UaImHU+S/SekwGLRLX1KfcTpnz1+TZiQqShG9vqU - B4dAge+imwAsZTCnI9H3tmz6jWekXQYRUraJUwjEaqqLoSQT5VQmEl518ueeRKKN - B/8mi1pylWqNUjedV4A5AgMBAAGjggJHMIICQzAOBgNVHQ8BAf8EBAMCBaAwHQYD - VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0O - BBYEFLUbMi65bMLlINPzTplLjtCHZfa0MB8GA1UdIwQYMBaAFBQusxe3WFbLrlAJ - QOYfr52LFMLGMFUGCCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDovL3Iz - Lm8ubGVuY3Iub3JnMCIGCCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5vcmcv - MBgGA1UdEQQRMA+CDSouYmlnYmFuZy5kZXYwTAYDVR0gBEUwQzAIBgZngQwBAgEw - NwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5j - cnlwdC5vcmcwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdQBElGUusO7Or8RAB9io - /ijA2uaCvtjLMbU/0zOWtbaBqAAAAXwn948JAAAEAwBGMEQCIBkkdKr6WRtmZYO8 - kuchAYDxGPaCnU9FYU3BZBpsbJvLAiButEYn4AvTFiZMILymyuuqct/eFjIR9MEE - pNotyaD+bQB2AH0+8viP/4hVaCTCwMqeUol5K8UOeAl/LmqXaJl+IvDXAAABfCf3 - kGUAAAQDAEcwRQIhAOOOX0qpI8xjqARUfU4ErGe8icHORlNHHzP/a6b3XE4ZAiBp - fMNh3oihXS1e6EM9Xs8m+9nuCi7rqLNSkCNuwisK7zANBgkqhkiG9w0BAQsFAAOC - AQEABMjkLKKxYyL4ZT6BPuOyqC4hnczDYUmZdCCysLu7psCjrZIAlSRxLIWXdWir - ogi/Vf+wdPKk38NDar0T9+rfAehuvQjQKCzIKVzr+MGauW0Wytwt63EgLIl2znvX - jWEIUwDQkqeFzPMbov8BK8hdLibBSz9nLrT0Zyw9mgRIzslemsi62+AjSNERTCTv - qyhinnBHLd3dGLOAXexwXu7ic2ZwCgnSgcli+MWC30QOh6ePJJqgw6OpwvOC9DAV - fkvGYFXlgYXnhQeLr0/4tzw3koclRWe/qgjAdAjB03yp1e53b+j9NoOfyobo1MFe - nMqEgcgAiA2VuE62Q4HE0Rs5wA== - -----END CERTIFICATE----- - -----BEGIN CERTIFICATE----- - MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw - TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh - cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw - WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg - RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK - AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP - R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx - sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm - NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg - Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG - /kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC - AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB - Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA - FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw - AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw - Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB - gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W - PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl - ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz - CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm - lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 - avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 - yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O - yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids - hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ - HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv - MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX - nLRbwHOoq7hHwg== - -----END CERTIFICATE----- - -----BEGIN CERTIFICATE----- - MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/ - MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT - DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow - TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh - cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB - AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC - ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL - wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D - LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK - 4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5 - bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y - sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ - Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4 - FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc - SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql - PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND - TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw - SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1 - c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx - +tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB - ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu - b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E - U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu - MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC - 5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW - 9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG - WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O - he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC - Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5 - -----END CERTIFICATE----- - values: - replicas: 1 - imagePullSecrets: - - name: "private-registry" - postgresql: - image: - pullSecrets: - - "private-registry" - istio: - enabled: true - keycloak: - enabled: true - resources: - requests: - cpu: "750m" - memory: "1024Mi" - limits: - cpu: "750m" - memory: "1024Mi" - startupProbe: | - httpGet: - path: /auth/realms/master - port: http - initialDelaySeconds: 600 - timeoutSeconds: 10 - failureThreshold: 60 - periodSeconds: 10 - secrets: - env: - stringData: - CUSTOM_REGISTRATION_CONFIG: /opt/jboss/keycloak/customreg.yaml - KEYCLOAK_IMPORT: /opt/jboss/keycloak/realm.json - X509_CA_BUNDLE: /etc/x509/https/cas.pem - certauthority: - stringData: - cas.pem: '{{ .Files.Get "resources/dev/dod_cas.pem" }}' - customreg: - stringData: - customreg.yaml: '{{ .Files.Get "resources/dev/baby-yoda.yaml" }}' - realm: - stringData: - realm.json: '{{ .Files.Get "resources/dev/baby-yoda.json" }}' - extraVolumes: |- - - name: certauthority - secret: - secretName: {{ include "keycloak.fullname" . }}-certauthority - - name: customreg - secret: - secretName: {{ include "keycloak.fullname" . }}-customreg - - name: realm - secret: - secretName: {{ include "keycloak.fullname" . }}-realm - extraVolumeMounts: |- - - name: certauthority - mountPath: /etc/x509/https/cas.pem - subPath: cas.pem - readOnly: true - - name: customreg - mountPath: /opt/jboss/keycloak/customreg.yaml - subPath: customreg.yaml - readOnly: true - - name: realm - mountPath: /opt/jboss/keycloak/realm.json - subPath: realm.json - readOnly: true - - - - # All the other add-ons that we aren't using - argocd: - enabled: false - authservice: - enabled: false - anchore: - enabled: false - minio: - enabled: false - git: - repo: http://stuart-gitea-http.git.svc.cluster.local:3000/zarf-git-user/mirror__repo1.dso.mil__platform-one__big-bang__apps__application-utilities__minio.git - tag: "4.2.3-bb.1" - values: - istio: - enabled: true - gateways: - - istio-system/public - velero: - enabled: false diff --git a/examples/software-factory/template/flux/kustomization.yaml b/examples/software-factory/template/flux/kustomization.yaml deleted file mode 100644 index 9bd3ca5a7a..0000000000 --- a/examples/software-factory/template/flux/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -bases: - - git::https://repo1.dso.mil/platform-one/big-bang/bigbang.git/base/flux?ref=tags/1.17.0 diff --git a/examples/software-factory/zarf.yaml b/examples/software-factory/zarf.yaml deleted file mode 100644 index 2572aee733..0000000000 --- a/examples/software-factory/zarf.yaml +++ /dev/null @@ -1,208 +0,0 @@ -kind: ZarfPackageConfig -metadata: - name: software-factory-demo - description: "Demo Zarf deployment of a software factory" - -components: - - name: flux - required: true - manifests: manifests/flux - images: - # Flux images - - registry1.dso.mil/ironbank/fluxcd/helm-controller:v0.11.0 - - registry1.dso.mil/ironbank/fluxcd/kustomize-controller:v0.13.0 - - registry1.dso.mil/ironbank/fluxcd/notification-controller:v0.15.0 - - registry1.dso.mil/ironbank/fluxcd/source-controller:v0.14.0 - - - name: bb-umbrella - required: true - manifests: manifests/bigbang - # 1. helm template bigbang ./chart | yq e '. | select(.kind == "GitRepository") | "- " + .spec.url + "@" + .spec.ref.tag' - - # 2. Add the actual bigbang repo as well - # https://repo1.dso.mil/platform-one/big-bang/bigbang/-/tags/1.17.0 - repos: - - https://repo1.dso.mil/platform-one/big-bang/bigbang.git@1.17.0 - - https://repo1.dso.mil/platform-one/big-bang/apps/core/cluster-auditor.git@0.3.0-bb.7 - - https://repo1.dso.mil/platform-one/big-bang/apps/core/policy.git@3.5.2-bb.1 - - https://repo1.dso.mil/platform-one/big-bang/apps/core/istio-controlplane.git@1.10.4-bb.3 - - https://repo1.dso.mil/platform-one/big-bang/apps/core/istio-operator.git@1.10.4-bb.1 - - https://repo1.dso.mil/platform-one/big-bang/apps/core/jaeger.git@2.23.0-bb.2 - - https://repo1.dso.mil/platform-one/big-bang/apps/core/kiali.git@1.39.0-bb.2 - - https://repo1.dso.mil/platform-one/big-bang/apps/core/eck-operator.git@1.6.0-bb.2 - - https://repo1.dso.mil/platform-one/big-bang/apps/core/elasticsearch-kibana.git@0.1.21-bb.0 - - https://repo1.dso.mil/platform-one/big-bang/apps/core/fluentbit.git@0.16.6-bb.0 - - https://repo1.dso.mil/platform-one/big-bang/apps/core/monitoring.git@14.0.0-bb.10 - - https://repo1.dso.mil/platform-one/big-bang/apps/security-tools/twistlock.git@0.0.9-bb.0 - - https://repo1.dso.mil/platform-one/big-bang/apps/developer-tools/sonarqube.git@9.6.3-bb.2 - - https://repo1.dso.mil/platform-one/big-bang/apps/developer-tools/gitlab.git@4.12.9-bb.6 - - https://repo1.dso.mil/platform-one/big-bang/apps/developer-tools/gitlab-runner.git@0.29.0-bb.1 - - https://repo1.dso.mil/platform-one/big-bang/apps/collaboration-tools/mattermost.git@0.2.4-bb.0 - - https://repo1.dso.mil/platform-one/big-bang/apps/collaboration-tools/mattermost-operator.git@1.16.0-bb.0 - - https://repo1.dso.mil/platform-one/big-bang/apps/developer-tools/nexus.git@36.0.0-bb.0 - - https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio-operator.git@4.1.2-bb.3 - - https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio.git@4.1.2-bb.6 - - https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio.git@4.2.3-bb.1 - - https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio-operator.git@4.2.3-bb.1 - # - https://repo1.dso.mil/platform-one/big-bang/apps/security-tools/keycloak.git@11.0.1-bb.6 - - images: - # TODO: Figure out a better way to derive this list. - # 1. Deploy Big Bang Core using some other method like https://repo1.dso.mil/platform-one/quick-start/big-bang - # 2. kubectl get pods --all-namespaces -o json | jq '.items[].spec.containers[].image' | jq -s 'unique' | yq e -P - # 3. Move all 'registry1.dso.mil/ironbank/fluxcd' images to the 'local.images' section - # 4. Add 'docker.io/' to any images that aren't fully qualified (example: rancher/metrics-server -> docker.io/rancher/metrics-server - # OR go through each values.yaml file in each git repo specified above and pull out all the images - - # common - - registry1.dso.mil/ironbank/big-bang/base:8.4 - - # cluster-auditor - - registry1.dso.mil/ironbank/cluster-auditor/opa-collector:0.3.2 - - # policy - - registry1.dso.mil/ironbank/opensource/kubernetes-1.21/kubectl:v1.21.1 - - registry1.dso.mil/ironbank/opensource/openpolicyagent/gatekeeper:v3.5.2 - - # istio-controlplane - - registry1.dso.mil/ironbank/opensource/istio/istioctl:1.10.4 - - registry1.dso.mil/ironbank/opensource/istio/install-cni:1.10.4 - - registry1.dso.mil/ironbank/opensource/istio/proxyv2:1.10.4 - - registry1.dso.mil/ironbank/opensource/istio/pilot:1.10.4 - - # istio-operator - - registry1.dso.mil/ironbank/opensource/istio/operator:1.10.4 - - # # Keycloak - # - registry.dso.mil/platform-one/big-bang/apps/security-tools/keycloak/keycloak-ib:14.0.0-1.0.6-1 - # - registry.dso.mil/platform-one/big-bang/apps/security-tools/keycloak/postgresql:11.8.0-debian-10-r61 - # - registry.dso.mil/platform-one/big-bang/apps/security-tools/keycloak/busybox:1.32 - - # jaeger - - registry1.dso.mil/ironbank/opensource/jaegertracing/jaeger-operator:1.24.0 - - registry1.dso.mil/ironbank/opensource/jaegertracing/jaeger-es-index-cleaner:1.24.0 - - registry1.dso.mil/ironbank/opensource/jaegertracing/all-in-one:1.24.0 - - registry1.dso.mil/ironbank/opensource/jaegertracing/jaeger-agent:1.24.0 - - registry1.dso.mil/ironbank/opensource/jaegertracing/jaeger-ingester:1.24.0 - - registry1.dso.mil/ironbank/opensource/jaegertracing/jaeger-query:1.24.0 - - registry1.dso.mil/ironbank/opensource/jaegertracing/jaeger-collector:1.24.0 - - # kiali - - registry1.dso.mil/ironbank/opensource/kiali/kiali-operator:v1.39.0 - - registry1.dso.mil/ironbank/opensource/kiali/kiali:v1.39.0 - - # eck-operator - - registry1.dso.mil/ironbank/elastic/eck-operator/eck-operator:1.6.0 - - # elasticsearch-kibana - - registry1.dso.mil/ironbank/elastic/kibana/kibana:7.12.0 - - registry1.dso.mil/ironbank/elastic/elasticsearch/elasticsearch:7.13.4 - - # fluentbit - - registry1.dso.mil/ironbank/opensource/fluent/fluent-bit:1.8.6 - - # monitoring - - registry1.dso.mil/ironbank/opensource/prometheus/alertmanager:v0.21.0 - - registry1.dso.mil/ironbank/opensource/grafana/grafana:7.5.2 - - registry1.dso.mil/ironbank/opensource/bats/bats:1.2.1 - - registry1.dso.mil/ironbank/kiwigrid/k8s-sidecar:1.10.6 - - registry1.dso.mil/ironbank/opensource/coreos/kube-state-metrics:v1.9.8 - - registry1.dso.mil/ironbank/opensource/prometheus/node-exporter:v1.0.1 - - registry1.dso.mil/ironbank/opensource/jet/kube-webhook-certgen:v1.5.1 - - registry1.dso.mil/ironbank/opensource/prometheus-operator/prometheus-operator:v0.46.0 - - registry1.dso.mil/ironbank/opensource/jimmidyson/configmap-reload:v0.5.0 - - registry1.dso.mil/ironbank/opensource/prometheus-operator/prometheus-config-reloader:v0.46.0 - - registry1.dso.mil/ironbank/opensource/kubernetes-1.20/kubectl-1.20:v1.20.8 - - registry1.dso.mil/ironbank/opensource/prometheus/prometheus:v2.25.0 - - # twistlock - - registry1.dso.mil/ironbank/twistlock/console/console:21.04.439 - - # sonarqube - - registry1.dso.mil/ironbank/big-bang/sonarqube:8.9-community - - registry1.dso.mil/ironbank/opensource/postgres/postgresql96:9.6.20 - - registry.dso.mil/platform-one/big-bang/apps/developer-tools/sonarqube/postgresql:11.7.0-debian-10-r26 - - # gitlab - - registry1.dso.mil/ironbank/gitlab/gitlab/alpine-certificates:13.12.9 - - registry1.dso.mil/ironbank/gitlab/gitlab-runner/gitlab-runner:v13.12.0 - - registry1.dso.mil/ironbank/gitlab/gitlab/kubectl:13.12.9 - - registry1.dso.mil/ironbank/redhat/ubi/ubi8:8.4 - - registry1.dso.mil/ironbank/bitnami/analytics/redis-exporter:1.18.0 - - registry1.dso.mil/ironbank/opensource/redis/redis5:5.0.9 - - registry.dso.mil/platform-one/big-bang/apps/developer-tools/gitlab/postgresql:11.9.0 - - registry1.dso.mil/ironbank/gitlab/gitlab/gitlab-container-registry:13.12.9 - - registry1.dso.mil/ironbank/gitlab/gitlab/cfssl-self-sign:1.4.1 - - registry1.dso.mil/ironbank/gitlab/gitlab/gitlab-task-runner:13.12.9 - - registry1.dso.mil/ironbank/gitlab/gitlab/gitlab-exporter:13.12.9 - - registry1.dso.mil/ironbank/gitlab/gitlab/gitlab-webservice:13.12.9 - - registry1.dso.mil/ironbank/gitlab/gitlab/gitlab-workhorse:13.12.9 - - registry1.dso.mil/ironbank/gitlab/gitlab/gitlab-sidekiq:13.12.9 - - registry1.dso.mil/ironbank/gitlab/gitlab/gitaly:13.12.9 - - registry1.dso.mil/ironbank/gitlab/gitlab/gitlab-shell:13.12.9 - - registry1.dso.mil/ironbank/opensource/minio/minio:RELEASE.2021-04-06T23-11-00Z - - registry1.dso.mil/ironbank/opensource/minio/mc:RELEASE.2021-03-23T05-46-11Z - - docker.io/rancher/pause:3.1 - - # minio & minio-operator - - registry1.dso.mil/ironbank/opensource/minio/operator:v4.1.2 - - registry1.dso.mil/ironbank/opensource/minio/minio:RELEASE.2021-08-31T05-46-54Z - - registry1.dso.mil/ironbank/opensource/minio/mc:RELEASE.2021-09-02T09-21-27Z - - # mattermost & mattermost-operator - - registry1.dso.mil/ironbank/opensource/mattermost/mattermost:5.39.0 - - registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator:v1.16.0 - - registry1.dso.mil/ironbank/opensource/postgres/postgresql11:11.10 - - registry1.dso.mil/ironbank/opensource/postgres/postgresql12:12.8 - - - # nexus - - registry1.dso.mil/ironbank/redhat/ubi/ubi8-minimal:8.4 - - registry1.dso.mil/ironbank/sonatype/nexus/nexus:3.36.0-01 - - - name: atlassian - required: true - manifests: manifests/atlassian - charts: - - name: jira - url: https://repo1.dso.mil/platform-one/big-bang/apps/third-party/jira.git - version: 0.1.0-bb.7 - - name: confluence - url: https://repo1.dso.mil/platform-one/big-bang/apps/third-party/confluence.git - version: 0.1.0-bb.9 - files: - # We need jq because the script uses it to check whether Istio is running yet - - source: https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 - shasum: af986793a515d500ab2d35f8d2aecd656e764504b789b66d7e1a0b727a124c44 - target: ./jq - executable: true - images: - # jira - - registry1.dso.mil/ironbank/atlassian/jira-data-center/jira-node:8.18.1 - # confluence - - registry1.dso.mil/ironbank/atlassian/confluence-data-center/confluence-node:7.13.0 - - registry1.dso.mil/ironbank/redhat/ubi/ubi7-minimal:7.9 - scripts: - retry: true - before: - # Check to see if istiod is running before trying to deploy. If Istio is running then so is Gatekeeper. This is a poor man's version of "DependsOn" since we aren't doing real gitops yet - - | - test $(/usr/local/bin/kubectl get pods -n istio-system -l app=istiod --field-selector=status.phase=Running -o json | ./jq -r '.items | length') -gt 0 - - - name: jenkins - required: true - manifests: manifests/jenkins - charts: - - name: jenkins - url: https://charts.jenkins.io - version: 3.9.4 - images: - - jenkins/jenkins:2.319.1-jdk11 - - kiwigrid/k8s-sidecar:1.14.2 - - jenkins/inbound-agent:4.11-1 - - maorfr/kube-tasks:0.2.0 - scripts: - retry: true - before: - # Check to see if istiod is running before trying to deploy. If Istio is running then so is Gatekeeper. This is a poor man's version of "DependsOn" since we aren't doing real gitops yet - - | - test $(/usr/local/bin/kubectl get pods -n istio-system -l app=istiod --field-selector=status.phase=Running -o json | ./jq -r '.items | length') -gt 0 diff --git a/examples/tiny-kafka/README.md b/examples/tiny-kafka/README.md index e112b14aab..e5fdf927c8 100644 --- a/examples/tiny-kafka/README.md +++ b/examples/tiny-kafka/README.md @@ -1,16 +1,136 @@ -This is a sample package that deploys Kafka onto K3s using Iron Bank images. +# Zarf Tiny Kafka Example -Steps to use: +This example demonstrates using Zarf to deploy a simple operator example, in this case [Strimzi Kafka Operator](https://strimzi.io/). -1. Download the Zarf release, https://repo1.dso.mil/platform-one/big-bang/apps/product-tools/zarf/-/releases -2. Run `zarf package create` in this folder on the online machine -3. Copy the created `zarf-package-kafka-strimzi-demo.tar.zst` file and the other download zarf files to the offline/airgap/test machine -4. Run `zarf init` with all defaults -5. Run `zarf package deploy` and choose the package from step 3. +## The Flow -Testing will require JDK and the kafka tools: `sudo apt install openjdk-14-jdk-headless` for Ubuntu. More details can be found at https://kafka.apache.org/quickstart. Steps to test: +Here's what you'll do in this example: -1. Install JDK and extract the Kafka tools from the package `/opt/kafka.tgz` +1. [Get ready](#get-ready) + +1. [Create a cluster](#create-a-cluster) + +1. [Package it](#package-it) + +1. [Deploy it](#deploy-it) + +1. [Try it](#try-it) + +1. [Cleanup](#cleanup) + +  + +## Get ready + +Before the magic can happen you have to do a few things: + +1. Install [Docker](https://docs.docker.com/get-docker/). Other container engines will likely work as well but aren't actively tested by the Zarf team. + +2. Install [KinD](https://github.com/kubernetes-sigs/kind). Other Kubernetes distros will work as well, but we'll be using KinD for this example since it is easy and tested frequently and thoroughly. + +3. Clone the Zarf project — for the example configuration files. + +4. Download a Zarf release — you need a binary _**and**_ an init package, [here](../../docs/workstation.md#just-gimmie-zarf). + +  + +## Create a cluster + +You can't run software without _somewhere to run it_, so the first thing to do is create a local Kubernetes cluster that Zarf can deploy to. In this example we'll be using KinD to create a lightweight, local K8s cluster running in Docker. + +Kick that off by running this command: + +```sh +kind create cluster +``` + +This will result in a single-node Kubernetes cluster called `kind-kind` on your local machine running in Docker. Your KUBECONFIG should be automatically configured to talk to the new cluster. + +```sh +cd +zarf init +``` + +Follow the prompts, answering "no" to each of the optional components, since we don't need them for this deployment. + +Congratulations! Your machine is now running a single-node Kubernetes cluster powered by Zarf! + +> _**Note**_ +> +> Zarf supports non-interactive installs too! Give `zarf init --confirm --components logging` a try next time. + +**Troubleshooting:** + +> _**ERROR: Unable to find the package on the local system, expected package at zarf-init.tar.zst**_ +> +> The zarf binary needs an init package to know how to setup your cluster! So, if `zarf init` returns an error like this: +> +> ```sh +> ERROR: Unable to find the package on the local system, expected package at zarf-init.tar.zst +> ``` +> +> It's likely you've either forgotten to download `zarf-init.tar.zst` (as part of [getting ready](#get-ready)) _**OR**_ you are _not_ running `zarf init` from the directory the init package is sitting in. + +> _**ERROR: failed to create cluster: node(s) already exist for a cluster with the name "kind"**_ +> +> You already have a KinD cluster running. Either just move on to use the current cluster, or run `kind delete cluster`, then `kind create cluster`. + +> _**Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?**_ +> +> Docker isn't running or you're otherwise not able to talk to it. Check your Docker installation, then try again. + +  + +## Package it + +Zarf is (at heart) a tool for making it easy to get software from _where you have it_ to _**where you need it**_—specifically, across an airgap. Since moving bits is so core to Zarf the idea of a "ready-to-move group of software" has a specific name—the _package_. + +All of the software a Zarf cluster runs is installed via package—for many reasons like versioning, auditability, etc—which means that if you want to run a in your cluster you're going to have to build a package for it. + +Luckily, this is very easy to do—package contents are defined by simple, declarative yaml files and _we've already made one for you_. To build this package you simply: + +```sh +cd /examples/tiny-kafka # directory with zarf.yaml, and +zarf package create --confirm # make the package +``` + +Watch the terminal scroll for a while. Once things are downloaded & zipped up and you'll see a file ending in `.tar.zst` drop. _That's_ your package. + +  + +## Deploy it + +It's time to feed the package you built into your cluster. + +Since you're running a Zarf cluster directly on your local machine—where this package & `zarf` binary _already are_—deploying the package is very simple: + +```sh +zarf package deploy zarf-package-kafka-strimzi-demo.tar.zst --confirm +``` + +In a couple seconds the cluster will have loaded your package. + +  + +## Use it + +Testing will require JDK and the kafka tools: `sudo apt install openjdk-14-jdk-headless` for Ubuntu. More details can be found at https://kafka.apache.org/quickstart. Steps to test: + +1. Install JDK and extract the Kafka tools from the package `kafka.tgz` 2. Get the Nodeport: `NODEPORT=$(kubectl get service demo-kafka-external-bootstrap -n kafka-demo -o=jsonpath='{.spec.ports[0].nodePort}{"\n"}')` 3. For pub: `./bin/kafka-console-producer.sh --broker-list localhost:$NODEPORT --topic cool-topic` 4. For sub: `./bin/kafka-console-consumer.sh --bootstrap-server localhost:$NODEPORT --topic cool-topic` + +  + +## Cleanup + +Once you've had your fun it's time to clean up. + +In this case, since the Zarf cluster was installed specifically (and _only_) to serve this example, clean up is really easy—you just tear down the entire cluster: + +```sh +kind delete cluster +``` + +It only takes a couple moments for the _entire cluster_ to disappear—long-running system services and all—leaving your machine ready for the next adventure. diff --git a/examples/tiny-kafka/charts/strimzi-values.yaml b/examples/tiny-kafka/charts/strimzi-values.yaml new file mode 100644 index 0000000000..3416edb04a --- /dev/null +++ b/examples/tiny-kafka/charts/strimzi-values.yaml @@ -0,0 +1,6 @@ +image: + imagePullSecrets: zarf-registry +imageRegistryOverride: registry1.dso.mil +imageRepositoryOverride: ironbank/opensource/strimzi +watchNamespaces: + - kafka-demo \ No newline at end of file diff --git a/examples/tiny-kafka/manifests/image-pull-secret.yaml b/examples/tiny-kafka/manifests/image-pull-secret.yaml deleted file mode 100644 index 52685465ee..0000000000 --- a/examples/tiny-kafka/manifests/image-pull-secret.yaml +++ /dev/null @@ -1,55 +0,0 @@ -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: kafka-operator -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } ---- -apiVersion: v1 -kind: Secret -type: kubernetes.io/dockerconfigjson -metadata: - name: private-registry - namespace: kafka-demo -stringData: - .dockerconfigjson: | - { - "auths": { - "registry.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry1.dso.mil": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "registry-1.docker.io": { - "auth":"###ZARF_DOCKERAUTH###" - }, - "ghcr.io": { - "auth":"###ZARF_DOCKERAUTH###" - } - } - } diff --git a/examples/tiny-kafka/manifests/kafka.yaml b/examples/tiny-kafka/manifests/kafka.yaml index 6f8d27a971..caa33fc95f 100644 --- a/examples/tiny-kafka/manifests/kafka.yaml +++ b/examples/tiny-kafka/manifests/kafka.yaml @@ -1,8 +1,3 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: kafka-demo ---- apiVersion: kafka.strimzi.io/v1beta2 kind: Kafka metadata: diff --git a/examples/tiny-kafka/manifests/operator.yaml b/examples/tiny-kafka/manifests/operator.yaml deleted file mode 100644 index 10fc6f861c..0000000000 --- a/examples/tiny-kafka/manifests/operator.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: kafka-operator ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: strimzi - namespace: kafka-operator -spec: - chart: https://%{KUBERNETES_API}%/static/charts/strimzi-kafka-operator-0.24.0.tgz - targetNamespace: kafka-operator - valuesContent: |- - image: - imagePullSecrets: private-registry - imageRegistryOverride: registry1.dso.mil - imageRepositoryOverride: ironbank/opensource/strimzi - watchNamespaces: - - kafka-demo diff --git a/examples/tiny-kafka/zarf.yaml b/examples/tiny-kafka/zarf.yaml index 4d907fa742..6d3b6526b0 100644 --- a/examples/tiny-kafka/zarf.yaml +++ b/examples/tiny-kafka/zarf.yaml @@ -6,13 +6,18 @@ metadata: components: - name: baseline required: true - manifests: manifests - + manifests: + - name: kafka-config + files: + - manifests/kafka.yaml + - manifests/kafka-topic.yaml charts: - name: strimzi-kafka-operator url: https://strimzi.io/charts/ version: 0.24.0 - + namespace: kafka-operator + valuesFiles: + - charts/strimzi-values.yaml images: - registry1.dso.mil/ironbank/opensource/strimzi/operator:0.24.0 - registry1.dso.mil/ironbank/opensource/strimzi/kafka:0.24.0-kafka-2.8.0 @@ -22,4 +27,4 @@ components: files: - source: https://archive.apache.org/dist/kafka/2.8.0/kafka_2.13-2.8.0.tgz shasum: 3fa380ae5d1385111ee9c83b0d1806172924ffec2e29399fd1a42671a97492c6 - target: /opt/kafka.tgz + target: kafka.tgz diff --git a/go.mod b/go.mod index fcdd7fbc23..fd360b00f8 100644 --- a/go.mod +++ b/go.mod @@ -4,22 +4,58 @@ go 1.16 require ( github.com/AlecAivazis/survey/v2 v2.3.2 - github.com/alecthomas/jsonschema v0.0.0-20211022214203-8b29eab41725 - github.com/docker/cli v20.10.10+incompatible + github.com/alecthomas/jsonschema v0.0.0-20211228220459-151e3c21f49d + github.com/derailed/k9s v0.25.18 + github.com/distribution/distribution/v3 v3.0.0-20210804104954-38ab4c606ee3 + github.com/docker/cli v20.10.12+incompatible github.com/fatih/color v1.13.0 github.com/go-git/go-git/v5 v5.4.2 - github.com/goccy/go-yaml v1.9.4 - github.com/google/go-containerregistry v0.7.0 + github.com/go-logr/logr v1.2.2 + github.com/goccy/go-yaml v1.9.5 + github.com/google/go-containerregistry v0.8.0 github.com/gruntwork-io/terratest v0.38.2 - github.com/mattn/go-colorable v0.1.11 + github.com/mattn/go-colorable v0.1.12 github.com/mholt/archiver/v3 v3.5.1 github.com/otiai10/copy v1.7.0 - github.com/sirupsen/logrus v1.8.1 - github.com/spf13/cobra v1.2.1 + github.com/pterm/pterm v0.12.33 + github.com/spf13/cobra v1.3.0 github.com/stretchr/testify v1.7.0 - golang.org/x/crypto v0.0.0-20211202192323-5770296d904e + golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e helm.sh/helm/v3 v3.7.2 - k8s.io/api v0.22.4 - k8s.io/apimachinery v0.22.4 - k8s.io/client-go v0.22.4 + k8s.io/api v0.22.5 + k8s.io/apimachinery v0.22.5 + k8s.io/client-go v0.22.5 + k8s.io/klog/v2 v2.40.1 + sigs.k8s.io/kustomize/api v0.8.11 + sigs.k8s.io/kustomize/kyaml v0.11.0 + sigs.k8s.io/yaml v1.3.0 +) + +replace ( + // https://github.com/kubernetes/kubernetes/issues/79384#issuecomment-505627280 + k8s.io/api => k8s.io/api v0.22.5 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.22.5 // indirect + k8s.io/apimachinery => k8s.io/apimachinery v0.22.5 // indirect + k8s.io/apiserver => k8s.io/apiserver v0.22.5 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.22.5 + k8s.io/client-go => k8s.io/client-go v0.22.5 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.22.5 + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.22.5 + k8s.io/code-generator => k8s.io/code-generator v0.22.5 + k8s.io/component-base => k8s.io/component-base v0.22.5 + k8s.io/component-helpers => k8s.io/component-helpers v0.22.5 + k8s.io/controller-manager => k8s.io/controller-manager v0.22.5 + k8s.io/cri-api => k8s.io/cri-api v0.22.5 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.22.5 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.22.5 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.22.5 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.22.5 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.22.5 + k8s.io/kubectl => k8s.io/kubectl v0.22.5 + k8s.io/kubelet => k8s.io/kubelet v0.22.5 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.22.5 + k8s.io/metrics => k8s.io/metrics v0.22.5 + k8s.io/mount-utils => k8s.io/mount-utils v0.22.5 + k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.22.5 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.22.5 ) diff --git a/go.sum b/go.sum index 7a3218d014..2c856a138c 100644 --- a/go.sum +++ b/go.sum @@ -25,8 +25,10 @@ cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWc cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= +cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -36,6 +38,7 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -56,18 +59,15 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= @@ -79,8 +79,15 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= +github.com/MarvinJWendt/testza v0.2.10 h1:cX4zE9TofXxe72a6EPIYAxC+8cVWTsmmgsXTZIT+5bQ= +github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -92,6 +99,7 @@ github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZC github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Masterminds/squirrel v1.5.2 h1:UiOEi2ZX4RCSkpiNDQN5kro/XIBpSRk9iTqdIRPzUXE= github.com/Masterminds/squirrel v1.5.2/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= @@ -113,8 +121,9 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3 github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.21 h1:btRfUDThBE5IKcvI8O8jOiIkujUsAMBSRsYDYmEi6oM= github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.23 h1:47MSwtKGXet80aIn+7h4YI6fwPmwIghAnsx2aOUrG2M= +github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -130,13 +139,19 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= +github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= -github.com/alecthomas/jsonschema v0.0.0-20211022214203-8b29eab41725 h1:NjwIgLQlD46o79bheVG4SCdRnnOz4XtgUN1WABX5DLA= -github.com/alecthomas/jsonschema v0.0.0-20211022214203-8b29eab41725/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60= +github.com/alecthomas/jsonschema v0.0.0-20211228220459-151e3c21f49d h1:4BQNwS4T13UU3Yee4GfzZH3Q9SNpKeJvLigfw8fDjX0= +github.com/alecthomas/jsonschema v0.0.0-20211228220459-151e3c21f49d/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -148,6 +163,8 @@ github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= @@ -156,17 +173,27 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkE github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/atomicgo/cursor v0.0.1 h1:xdogsqa6YYlLfM+GyClC/Lchf7aiMerFiZQn7soTOoU= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.35.21/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/aws/aws-sdk-go v1.40.56 h1:FM2yjR0UUYFzDTMx+mH9Vyw1k1EUUxsAFzk+BjkzANA= github.com/aws/aws-sdk-go v1.40.56/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -174,6 +201,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= @@ -193,13 +221,21 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= +github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= @@ -211,6 +247,9 @@ github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLI github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -219,11 +258,14 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= @@ -252,14 +294,16 @@ github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM= github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/containerd v1.5.8 h1:NmkCC1/QxyZFBny8JogwLpOy2f+VEbO/f6bV2Mqtwuw= +github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -288,13 +332,14 @@ github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFY github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/stargz-snapshotter/estargz v0.7.0/go.mod h1:83VWDqHnurTKliEB0YvWMiCfLDwv4Cjj1X9Vk98GJZw= -github.com/containerd/stargz-snapshotter/estargz v0.10.0 h1:glqzafvxBBAMo+x2w2sdDjUDZeTqqLJmqZPY05qehCU= -github.com/containerd/stargz-snapshotter/estargz v0.10.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= +github.com/containerd/stargz-snapshotter/estargz v0.10.1 h1:hd1EoVjI2Ax8Cr64tdYqnJ4i4pZU49FkEf5kU8KxQng= +github.com/containerd/stargz-snapshotter/estargz v0.10.1/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= @@ -349,6 +394,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/derailed/k9s v0.25.18 h1:wvTQ56NdJ2GALD1OQjQL34yhcB7sPRuDYo9py1Md+Vw= +github.com/derailed/k9s v0.25.18/go.mod h1:SPo0YasThaL9sz3/Ero9CfNM6QSkhLa3Cf+d606zrL8= +github.com/derailed/popeye v0.9.8 h1:53Rdx09WloOj6ltZZq9OeS48zH0F44mEMcs8XaI1g0Q= +github.com/derailed/popeye v0.9.8/go.mod h1:Ih3wTG7wBOuxdqz5tlCuCFq/vyB+Te/IpqY5HwgUTEA= +github.com/derailed/tview v0.6.6 h1:hNqBewhRTYRgfLp1p5KGw0DFdbGMS68iocBSmGGNg4s= +github.com/derailed/tview v0.6.6/go.mod h1:A1LXWlbx/YDMXr3GVTy+IgclAkBssJpw/FiZ7aqUgzU= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= @@ -358,16 +409,16 @@ github.com/distribution/distribution/v3 v3.0.0-20210804104954-38ab4c606ee3 h1:rE github.com/distribution/distribution/v3 v3.0.0-20210804104954-38ab4c606ee3/go.mod h1:gt38b7cvVKazi5XkHvINNytZXgTEntyhtyM3HQz46Nk= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v20.10.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.10+incompatible h1:kcbwdgWbrBOH8QwQzaJmyriHwF7XIl4HT1qh0HTRys4= -github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.12+incompatible h1:lZlz0uzG+GH+c0plStMUdF/qk3ppmgnswpR5EbqzVGA= +github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.10+incompatible h1:GKkP0T7U4ks6X3lmmHKC2QDprnpRJor2Z5a8m62R9ZM= -github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U= +github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= @@ -383,7 +434,6 @@ github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= @@ -391,6 +441,10 @@ github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj6 github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 h1:yY9rWGoXv1U5pl4gxqlULARMQD7x0QG85lqEXTWysik= github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= @@ -399,6 +453,7 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -407,12 +462,14 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= @@ -424,16 +481,26 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo= +github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.2.1/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= +github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM= +github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= @@ -455,24 +522,23 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= @@ -483,6 +549,7 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -498,20 +565,22 @@ github.com/gobuffalo/packr/v2 v2.8.1 h1:tkQpju6i3EtMXJ9uoF5GT6kB+LMTimDWD8Xvbz6z github.com/gobuffalo/packr/v2 v2.8.1/go.mod h1:c/PLlOuTU+p3SybaJATW3H6lX/iK7xEz5OeMf+NnJpg= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-yaml v1.9.4 h1:S0GCYjwHKVI6IHqio7QWNKNThUl6NLzFd/g8Z65Axw8= -github.com/goccy/go-yaml v1.9.4/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= +github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0= +github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= +github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -553,6 +622,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -577,8 +647,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-containerregistry v0.6.0/go.mod h1:euCCtNbZ6tKqi1E72vwDj2xZcN5ttKpZLfa/wSo5iLw= -github.com/google/go-containerregistry v0.7.0 h1:u0onUUOcyoCDHEiJoyR1R1gx5er1+r06V5DBhUU5ndk= -github.com/google/go-containerregistry v0.7.0/go.mod h1:2zaoelrL0d08gGbpdP3LqyUuBmhWbpD6IOe2s9nLS2k= +github.com/google/go-containerregistry v0.8.0 h1:mtR24eN6rapCN+shds82qFEIWWmg64NPMuyCNT7/Ogc= +github.com/google/go-containerregistry v0.8.0/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -611,14 +681,18 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= +github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= @@ -642,35 +716,54 @@ github.com/gruntwork-io/go-commons v0.8.0/go.mod h1:gtp0yTtIBExIZp7vyIV9I0XQkVwi github.com/gruntwork-io/terratest v0.38.2 h1:XgDGMxX+dE8Aw96wI8QH6oIzveej01Yk4bTjt6dtzIU= github.com/gruntwork-io/terratest v0.38.2/go.mod h1:XzW8PL9pAGbLyiBdQ5OiAeWSNpZ/9ycItjYstSS2PV8= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.9.1 h1:eOy4gREY0/ZQHNItlfuEZqtcQbXIxzojlP301hDpnac= github.com/hashicorp/hcl/v2 v2.9.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/terraform-json v0.12.0 h1:8czPgEEWWPROStjkWPUnTQDXmpmZPlkQAwYYLETaTvw= github.com/hashicorp/terraform-json v0.12.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= @@ -679,8 +772,10 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -691,6 +786,7 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= @@ -699,6 +795,7 @@ github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt7 github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -713,9 +810,12 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -753,9 +853,8 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -773,9 +872,17 @@ github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -794,13 +901,15 @@ github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlW github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= @@ -808,8 +917,11 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.11/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= @@ -825,10 +937,13 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.1.1/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4= @@ -847,6 +962,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -860,15 +977,16 @@ github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2J github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 h1:yH0SvLzcbZxcJXho2yh7CqdENGMQe73Cw3woZBpPli0= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -879,12 +997,21 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= @@ -892,19 +1019,21 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6 github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -913,8 +1042,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7 h1:axgApq2XShTLwQii2zAnIkMPlhGVHbAXHUcHezu5G/k= -github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5 h1:q37d91F6BO4Jp1UqWiun0dUFYaqv6WsKTLTCaWv+8LY= +github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -931,7 +1060,14 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/oracle/oci-go-sdk v7.1.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE= github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U= @@ -940,14 +1076,24 @@ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6 github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.3 h1:7JgpsBaN0uMkyju4tbYHu0mnM55hNKVYLsXmwr15NQI= github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/petergtz/pegomock v2.9.0+incompatible h1:BKfb5XfkJfehe5T+O1xD4Zm26Sb9dnRj7tHxLYwUPiI= +github.com/petergtz/pegomock v2.9.0+incompatible/go.mod h1:nuBLWZpVyv/fLo56qTwt/AUau7jgouO1h7bEvZCq82o= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -955,37 +1101,50 @@ github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -993,15 +1152,31 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= +github.com/pterm/pterm v0.12.33 h1:XiT50Pvdqn5O8FAiIqZMpXP6NkVEcmlUa+mkA1yWVCg= +github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= +github.com/rakyll/hey v0.1.4 h1:hhc8GIqHN4+rPFZvkM9lkCQGi7da0sINM83xxpFkbPA= +github.com/rakyll/hey v0.1.4/go.mod h1:nAOTOo+L52KB9SZq/M6J18kxjto4yVtXQDjU2HgjUPI= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.18.0/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I= +github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= +github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc h1:BD7uZqkN8CpjJtN/tScAKiccBikU4dlqe/gNrkRaPY4= github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc/go.mod h1:HFLT6i9iR4QBOF5rdCyjddC9t59ArqWJV2xx+jwcCMo= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= @@ -1011,6 +1186,10 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= +github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= +github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= @@ -1035,20 +1214,25 @@ github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:s github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= +github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -1061,8 +1245,12 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1088,6 +1276,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmccombs/hcl2json v0.3.3 h1:+DLNYqpWE0CsOQiEZu+OZm5ZBImake3wtITYxQ8uLFQ= github.com/tmccombs/hcl2json v0.3.3/go.mod h1:Y2chtz2x9bAeRTvSibVRVgbLJhLJXKlUeIvjeVdnm4w= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= @@ -1125,12 +1314,16 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= @@ -1143,21 +1336,27 @@ github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUA github.com/zclconf/go-cty v1.8.1 h1:SI0LqNeNxAgv2WWqWJMlG2/Ad/6aYJ7IVYYMigmfkuI= github.com/zclconf/go-cty v1.8.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -1182,11 +1381,15 @@ go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee33 go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1199,9 +1402,9 @@ golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1214,8 +1417,9 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e h1:1SzTfNOXwIS2oWiMF+6qu0OUDKb0dauo6MoDUQyu+yU= +golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1251,16 +1455,20 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181017193950-04a2e542c03f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1300,17 +1508,21 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1327,6 +1539,7 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1348,6 +1561,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1369,16 +1583,19 @@ golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1411,7 +1628,6 @@ golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1420,7 +1636,9 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1437,16 +1655,28 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 h1:WecRHqgE09JBkh/584XIE6PMz5KKE/vER4izNUi30AQ= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= +golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1454,17 +1684,18 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1477,13 +1708,16 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1494,6 +1728,7 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1527,12 +1762,15 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1561,8 +1799,13 @@ google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNe google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -1577,6 +1820,7 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -1607,7 +1851,6 @@ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1630,14 +1873,24 @@ google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211111162719-482062a4217b h1:qvEQEwKjZRAg6rjY/jqfJ7T8/w/D7jTIFJGcaSka96k= -google.golang.org/genproto v0.0.0-20211111162719-482062a4217b/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= @@ -1662,8 +1915,10 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM= +google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1691,6 +1946,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= @@ -1698,6 +1954,7 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -1726,8 +1983,10 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +helm.sh/helm/v3 v3.7.1/go.mod h1:3eOeBD3Z+O/ELiuu19zynZSN8jP1ErXLuyP21SZeMq8= helm.sh/helm/v3 v3.7.2 h1:xn1OxcZEpgKpp4CCpPz1KKUyb9gAtTouXV2E3S8ChYQ= helm.sh/helm/v3 v3.7.2/go.mod h1:UXuiAn0+FfBpqbiMuwWt8/aAKkfJvnWLBJ6f4HcFs0M= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1735,56 +1994,40 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.4 h1:UvyHW0ezB2oIgHAxlYoo6UJQObYXU7awuNarwoHEOjw= -k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk= -k8s.io/apiextensions-apiserver v0.22.4 h1:2iGpcVyw4MnAyyXVJU2Xg6ZsbIxAOfRHo0LF5A5J0RA= -k8s.io/apiextensions-apiserver v0.22.4/go.mod h1:kH9lxD8dbJ+k0ZizGET55lFgdGjO8t45fgZnCVdZEpw= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.4 h1:9uwcvPpukBw/Ri0EUmWz+49cnFtaoiyEhQTK+xOe7Ck= -k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.4 h1:L+220cy+94UWmyBl1kiVTklBXrBtKsbjlPV60eL2u6s= -k8s.io/apiserver v0.22.4/go.mod h1:38WmcUZiiy41A7Aty8/VorWRa8vDGqoUzDf2XYlku0E= -k8s.io/cli-runtime v0.22.4 h1:uFSVSdW14JP53BCtMRsw1hB9ba21TBuUb5m7RvEsH0Y= -k8s.io/cli-runtime v0.22.4/go.mod h1:x35r0ERHXr/MrbR1C6MPJxQ3xKG6+hXi9m2xLzlMPZA= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.4 h1:aAQ1Wk+I3bjCNk35YWUqbaueqrIonkfDPJSPDDe8Kfg= -k8s.io/client-go v0.22.4/go.mod h1:Yzw4e5e7h1LNHA4uqnMVrpEpUs1hJOiuBsJKIlRCHDA= -k8s.io/code-generator v0.22.4/go.mod h1:qjYl54pQ/emhkT0UxbufbREYJMWsHNNV/jSVwhYZQGw= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.4 h1:7qwLJnua2ppGNZrRGDQ0vhsFebI39VGbZ4zdR5ArViI= -k8s.io/component-base v0.22.4/go.mod h1:MrSaQy4a3tFVViff8TZL6JHYSewNCLshZCwHYM58v5A= -k8s.io/component-helpers v0.22.4/go.mod h1:A50qTyczDFbhZDifIfS2zFrHuPk9UNOWPpvNZ+3RSIs= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/api v0.22.5 h1:xk7C+rMjF/EGELiD560jdmwzrB788mfcHiNbMQLIVI8= +k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= +k8s.io/apiextensions-apiserver v0.22.5 h1:ML0QqT7FIlmZHN+9+2EtARJ3cJVHeoizt6GCteFRE0o= +k8s.io/apiextensions-apiserver v0.22.5/go.mod h1:tIXeZ0BrDxUb1PoAz+tgOz43Zi1Bp4BEEqVtUccMJbE= +k8s.io/apimachinery v0.22.5 h1:cIPwldOYm1Slq9VLBRPtEYpyhjIm1C6aAMAoENuvN9s= +k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= +k8s.io/apiserver v0.22.5 h1:71krQxCUz218ecb+nPhfDsNB6QgP1/4EMvi1a2uYBlg= +k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= +k8s.io/cli-runtime v0.22.5 h1:bZqLgx1INiPgXyMk/Hu3o5NFmdfvlvtsoE+wHJuKA2U= +k8s.io/cli-runtime v0.22.5/go.mod h1:12ah4O0kaevIYHsRcFGt8RKER0wlTN2yCgHp1c4Uxp4= +k8s.io/client-go v0.22.5 h1:I8Zn/UqIdi2r02aZmhaJ1hqMxcpfJ3t5VqvHtctHYFo= +k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= +k8s.io/code-generator v0.22.5/go.mod h1:sbdWCOVob+KaQ5O7xs8PNNaCTpbWVqNgA6EPwLOmRNk= +k8s.io/component-base v0.22.5 h1:U0eHqZm7mAFE42hFwYhY6ze/MmVaW00JpMrzVsQmzYE= +k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= +k8s.io/component-helpers v0.22.5/go.mod h1:UK4H16PcV6pTInkhAOfkPbN/aXHPXPX2/ZI4lfCXH4I= +k8s.io/cri-api v0.22.5/go.mod h1:uAw9CICQq20/1yB4ZnWT2TjJyMMROl4typFfWaURLwQ= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.40.1 h1:P4RRucWk/lFOlDdkAr3mc7iWFkgKrZY9qZMAgek06S4= +k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c h1:jvamsI1tn9V0S8jicyX82qaFC0H/NKxv2e5mbqsgR80= k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kubectl v0.22.4 h1:ECUO1QWyZ70DiIKEfgBx+8i9D98uspVOwgc1APs/07w= -k8s.io/kubectl v0.22.4/go.mod h1:ok2qRT6y2Gy4+y+mniJVyUMKeBHP4OWS9Rdtf/QTM5I= +k8s.io/kubectl v0.22.5 h1:diivOcs6dyDjpBqOpy9iiI3srZnW1khJDWwsFSapFt8= +k8s.io/kubectl v0.22.5/go.mod h1:uwKSKhaC6HOwnbk1cVLxVPYwfvazj9x06oZAOsL43N8= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/metrics v0.22.4/go.mod h1:6F/iwuYb1w2QDCoHkeMFLf4pwHBcYKLm4mPtVHKYrIw= +k8s.io/metrics v0.22.5 h1:2wNbA+Pk00Y+eJrfToksvckirugfbdWm3HQinwqDQFw= +k8s.io/metrics v0.22.5/go.mod h1:dCqOkoZQWLSfBhUtPFMiDrzJaPtXJlVePVuJbEx0hW8= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -1795,8 +2038,6 @@ rsc.io/letsencrypt v0.0.3 h1:H7xDfhkaFFSYEJlKeq38RwX2jYcnTeHuDQyT+mMNMwM= rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/kustomize/api v0.8.11 h1:LzQzlq6Z023b+mBtc6v72N2mSHYmN8x7ssgbf/hv0H8= sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g= @@ -1805,9 +2046,10 @@ sigs.k8s.io/kustomize/kustomize/v4 v4.2.0/go.mod h1:MOkR6fmhwG7hEDRXBYELTi5GSFcL sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA= sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/test/e2e/common.go b/test/e2e/common.go index 54dfdf8a19..2949ce959c 100644 --- a/test/e2e/common.go +++ b/test/e2e/common.go @@ -18,27 +18,75 @@ import ( "github.com/gruntwork-io/terratest/modules/terraform" ) -func teardown(t *testing.T, tmpFolder string) { - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - aws.DeleteEC2KeyPair(t, keyPair) +type ZarfE2ETest struct { + testing *testing.T + tempFolder string + username string + terraformOptions *terraform.Options + keyPair *aws.Ec2Keypair + publicIP string + publicHost ssh.Host +} + +func NewE2ETest(testing *testing.T) *ZarfE2ETest { + + testing.Parallel() + + // Copy the terraform folder to a temp directory so we can run multiple tests in parallel + tempFolder := teststructure.CopyTerraformFolderToTemp(testing, "..", "tf/public-ec2-instance") + + e2e := ZarfE2ETest{ + testing: testing, + tempFolder: tempFolder, + // Our SSH username, will change based on which AMI we use + username: "ubuntu", + } + + // Deploy the terraform infra + teststructure.RunTestStage(testing, "SETUP", e2e.setup) + + return &e2e +} - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - terraform.Destroy(t, terraformOptions) +func (e2e *ZarfE2ETest) runSSHCommand(format string, a ...interface{}) (string, error) { + command := fmt.Sprintf(format, a...) + return ssh.CheckSshCommandE(e2e.testing, e2e.publicHost, command) } -func setup(t *testing.T, tmpFolder string) { - terraformOptions, keyPair, err := configureTerraformOptions(t, tmpFolder) - require.NoError(t, err) +func (e2e *ZarfE2ETest) teardown() { + keyPair := teststructure.LoadEc2KeyPair(e2e.testing, e2e.tempFolder) + aws.DeleteEC2KeyPair(e2e.testing, keyPair) + + terraformOptions := teststructure.LoadTerraformOptions(e2e.testing, e2e.tempFolder) + terraform.Destroy(e2e.testing, terraformOptions) +} + +func (e2e *ZarfE2ETest) setup() { + terraformOptions, keyPair, err := e2e.configureTerraformOptions() + require.NoError(e2e.testing, err) // Save the options and key pair so later test stages can use them - teststructure.SaveTerraformOptions(t, tmpFolder, terraformOptions) - teststructure.SaveEc2KeyPair(t, tmpFolder, keyPair) + teststructure.SaveTerraformOptions(e2e.testing, e2e.tempFolder, terraformOptions) + teststructure.SaveEc2KeyPair(e2e.testing, e2e.tempFolder, keyPair) // This will run `terraform init` and `terraform apply` and fail the test if there are any errors - terraform.InitAndApply(t, terraformOptions) + terraform.InitAndApply(e2e.testing, terraformOptions) + + // Run `terraform output` to get the value of an output variable + e2e.publicIP = terraform.Output(e2e.testing, terraformOptions, "public_instance_ip") + e2e.terraformOptions = terraformOptions + e2e.keyPair = keyPair + + // We're going to try to SSH to the instance IP, using the Key Pair we created earlier, and the user "ubuntu", + // as we know the Instance is running an Ubuntu AMI that has such a user + e2e.publicHost = ssh.Host{ + Hostname: e2e.publicIP, + SshKeyPair: e2e.keyPair.KeyPair, + SshUserName: e2e.username, + } } -func configureTerraformOptions(t *testing.T, tmpFolder string) (*terraform.Options, *aws.Ec2Keypair, error) { +func (e2e *ZarfE2ETest) configureTerraformOptions() (*terraform.Options, *aws.Ec2Keypair, error) { // A unique ID we can use to namespace resources so we don't clash with anything already in the AWS account or // tests running in parallel uniqueID := random.UniqueId() @@ -56,13 +104,13 @@ func configureTerraformOptions(t *testing.T, tmpFolder string) (*terraform.Optio // Create an EC2 KeyPair that we can use for SSH access keyPairName := fmt.Sprintf("%s-%s-%s", namespace, stage, name) - keyPair := aws.CreateAndImportEC2KeyPair(t, awsRegion, keyPairName) + keyPair := aws.CreateAndImportEC2KeyPair(e2e.testing, awsRegion, keyPairName) // Construct the terraform options with default retryable errors to handle the most common retryable errors in // terraform testing. - terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + terraformOptions := terraform.WithDefaultRetryableErrors(e2e.testing, &terraform.Options{ // The path to where our Terraform code is located - TerraformDir: tmpFolder, + TerraformDir: e2e.tempFolder, // Variables to pass to our Terraform code using -var options Vars: map[string]interface{}{ @@ -80,48 +128,48 @@ func configureTerraformOptions(t *testing.T, tmpFolder string) (*terraform.Optio // syncFileToRemoteServer uses SCP to sync a file from source to destination. `destPath` can be absolute or relative to // the SSH user's home directory. It has to be in a directory that the SSH user is allowed to write to. -func syncFileToRemoteServer(t *testing.T, terraformOptions *terraform.Options, keyPair *aws.Ec2Keypair, sshUsername string, srcPath string, destPath string, chmod string) { +func (e2e *ZarfE2ETest) syncFileToRemoteServer(srcPath string, destPath string, chmod string) { // Run `terraform output` to get the value of an output variable - publicInstanceIP := terraform.Output(t, terraformOptions, "public_instance_ip") + publicInstanceIP := terraform.Output(e2e.testing, e2e.terraformOptions, "public_instance_ip") // We're going to try to SSH to the instance IP, using the Key Pair we created earlier, and the user "ubuntu", // as we know the Instance is running an Ubuntu AMI that has such a user host := ssh.Host{ Hostname: publicInstanceIP, - SshKeyPair: keyPair.KeyPair, - SshUserName: sshUsername, + SshKeyPair: e2e.keyPair.KeyPair, + SshUserName: e2e.username, } // It can take a minute or so for the Instance to boot up, so retry a few times maxRetries := 15 timeBetweenRetries, err := time.ParseDuration("5s") - require.NoError(t, err) + require.NoError(e2e.testing, err) // Wait for the instance to be ready - _, err = retry.DoWithRetryE(t, "Wait for the instance to be ready", maxRetries, timeBetweenRetries, func() (string, error) { - _, err := ssh.CheckSshCommandE(t, host, "whoami") + _, err = retry.DoWithRetryE(e2e.testing, "Wait for the instance to be ready", maxRetries, timeBetweenRetries, func() (string, error) { + _, err := ssh.CheckSshCommandE(e2e.testing, host, "whoami") if err != nil { return "", err } return "", nil }) - require.NoError(t, err) + require.NoError(e2e.testing, err) // Create the folder structure - output, err := ssh.CheckSshCommandE(t, host, fmt.Sprintf("bash -c 'install -m 644 -D /dev/null \"%s\"'", destPath)) - require.NoError(t, err, output) + output, err := ssh.CheckSshCommandE(e2e.testing, host, fmt.Sprintf("bash -c 'install -m 644 -D /dev/null \"%s\"'", destPath)) + require.NoError(e2e.testing, err, output) // The ssh lib only supports sending strings so we'll base64encode it first f, err := os.Open(srcPath) - require.NoError(t, err) + require.NoError(e2e.testing, err) reader := bufio.NewReader(f) content, err := ioutil.ReadAll(reader) - require.NoError(t, err) + require.NoError(e2e.testing, err) encodedContent := base64.StdEncoding.EncodeToString(content) - err = ssh.ScpFileToE(t, host, 0600, fmt.Sprintf("%s.b64", destPath), encodedContent) - require.NoError(t, err) - output, err = ssh.CheckSshCommandE(t, host, fmt.Sprintf("base64 -d \"%s.b64\" > \"%s\" && chmod \"%s\" \"%s\"", destPath, destPath, chmod, destPath)) - require.NoError(t, err, output) + err = ssh.ScpFileToE(e2e.testing, host, 0600, fmt.Sprintf("%s.b64", destPath), encodedContent) + require.NoError(e2e.testing, err) + output, err = ssh.CheckSshCommandE(e2e.testing, host, fmt.Sprintf("base64 -d \"%s.b64\" > \"%s\" && chmod \"%s\" \"%s\"", destPath, destPath, chmod, destPath)) + require.NoError(e2e.testing, err, output) } // getAwsRegion returns the desired AWS region to use by first checking the env var AWS_REGION, then checking diff --git a/test/e2e/e2e_data_injection_test.go b/test/e2e/e2e_data_injection_test.go index 5dadfd2470..d734770247 100644 --- a/test/e2e/e2e_data_injection_test.go +++ b/test/e2e/e2e_data_injection_test.go @@ -2,84 +2,42 @@ package test import ( "fmt" - "github.com/gruntwork-io/terratest/modules/aws" - "github.com/gruntwork-io/terratest/modules/ssh" - "github.com/gruntwork-io/terratest/modules/terraform" + "testing" + teststructure "github.com/gruntwork-io/terratest/modules/test-structure" "github.com/stretchr/testify/require" - "testing" ) func TestDataInjection(t *testing.T) { - t.Parallel() - // Our SSH username, will change based on which AMI we use - username := "ubuntu" - // Copy the terraform folder to a temp directory so we can run multiple tests in parallel - tmpFolder := teststructure.CopyTerraformFolderToTemp(t, "..", "tf/public-ec2-instance") + e2e := NewE2ETest(t) // At the end of the test, run `terraform destroy` to clean up any resources that were created - defer teststructure.RunTestStage(t, "TEARDOWN", func() { - teardown(t, tmpFolder) - }) - - // Deploy the terraform infra - teststructure.RunTestStage(t, "SETUP", func() { - setup(t, tmpFolder) - }) + defer teststructure.RunTestStage(e2e.testing, "TEARDOWN", e2e.teardown) // Upload the Zarf artifacts - teststructure.RunTestStage(t, "UPLOAD", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", username), "0700") - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf-init.tar.zst", fmt.Sprintf("/home/%s/build/zarf-init.tar.zst", username), "0600") - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf-package-data-injection-demo.tar", fmt.Sprintf("/home/%s/build/zarf-package-data-injection-demo.tar", username), "0600") + teststructure.RunTestStage(e2e.testing, "UPLOAD", func() { + e2e.syncFileToRemoteServer("../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", e2e.username), "0700") + e2e.syncFileToRemoteServer("../../build/zarf-init.tar.zst", fmt.Sprintf("/home/%s/build/zarf-init.tar.zst", e2e.username), "0600") + e2e.syncFileToRemoteServer("../../build/zarf-package-data-injection-demo.tar", fmt.Sprintf("/home/%s/build/zarf-package-data-injection-demo.tar", e2e.username), "0600") }) - teststructure.RunTestStage(t, "TEST", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) + teststructure.RunTestStage(e2e.testing, "TEST", func() { + // run `zarf init` + output, err := e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && ./zarf init --confirm --components k3s'", e2e.username) + require.NoError(e2e.testing, err, output) - // Finally run the actual test - runDataInjectionTest(t, terraformOptions, keyPair, username) - }) -} - -func runDataInjectionTest(t *testing.T, terraformOptions *terraform.Options, keyPair *aws.Ec2Keypair, username string) { - // Run `terraform output` to get the value of an output variable - publicInstanceIP := terraform.Output(t, terraformOptions, "public_instance_ip") - - // We're going to try to SSH to the instance IP, using the Key Pair we created earlier, and the user "ubuntu", - // as we know the Instance is running an Ubuntu AMI that has such a user - publicHost := ssh.Host{ - Hostname: publicInstanceIP, - SshKeyPair: keyPair.KeyPair, - SshUserName: username, - } - - // run `zarf init` - output, err := ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf init --confirm --components management --host 127.0.0.1'", username)) - require.NoError(t, err, output) + // Deploy the data injection example + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && ./zarf package deploy zarf-package-data-injection-demo.tar --confirm'", e2e.username) + require.NoError(e2e.testing, err, output) - // Wait until the Docker registry is ready - output, err = ssh.CheckSshCommandE(t, publicHost, "timeout 300 bash -c 'while [[ \"$(curl -sfSL --retry 15 --retry-connrefused --retry-delay 5 -o /dev/null -w \"%{http_code}\" \"https://127.0.0.1/v2/\")\" != \"401\" ]]; do sleep 1; done' || false") - require.NoError(t, err, output) + // Test to confirm the root file was placed + output, err = e2e.runSSHCommand(`sudo bash -c '/usr/local/bin/kubectl -n demo exec data-injection -- ls /test | grep this-is-an-example'`) + require.NoError(e2e.testing, err, output) - // Deploy the data injection example - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf package deploy zarf-package-data-injection-demo.tar --confirm'", username)) - require.NoError(t, err, output) - - // Wait until the deployment is ready - output, err = ssh.CheckSshCommandE(t, publicHost, `timeout 300 sudo bash -c 'while [ "$(/usr/local/bin/kubectl get pods -n demo -l app=data-injection --field-selector=status.phase=Running -o json | jq -r '"'"'.items | length'"'"')" -lt "1" ]; do sleep 1; done' || false`) - require.NoError(t, err, output) - - // Test to confirm the root file was placed - output, err = ssh.CheckSshCommandE(t, publicHost, `sudo bash -c '/usr/local/bin/kubectl -n demo exec data-injection -- ls /test | grep this-is-an-example'`) - require.NoError(t, err, output) + // Test to confirm the subdirectory file was placed + output, err = e2e.runSSHCommand(`sudo bash -c '/usr/local/bin/kubectl -n demo exec data-injection -- ls /test/subdirectory-test | grep this-is-an-example'`) + require.NoError(e2e.testing, err, output) + }) - // Test to confirm the subdirectory file was placed - output, err = ssh.CheckSshCommandE(t, publicHost, `sudo bash -c '/usr/local/bin/kubectl -n demo exec data-injection -- ls /test/subdirectory-test | grep this-is-an-example'`) - require.NoError(t, err, output) } diff --git a/test/e2e/e2e_example_game_test.go b/test/e2e/e2e_example_game_test.go index 893e245066..5a9e3852b5 100644 --- a/test/e2e/e2e_example_game_test.go +++ b/test/e2e/e2e_example_game_test.go @@ -3,85 +3,51 @@ package test import ( "fmt" "testing" + "time" - "github.com/gruntwork-io/terratest/modules/aws" - "github.com/gruntwork-io/terratest/modules/ssh" - "github.com/gruntwork-io/terratest/modules/terraform" teststructure "github.com/gruntwork-io/terratest/modules/test-structure" "github.com/stretchr/testify/require" ) func TestE2eExampleGame(t *testing.T) { - t.Parallel() - // Our SSH username, will change based on which AMI we use - username := "ubuntu" - - // Copy the terraform folder to a temp directory so we can run multiple tests in parallel - tmpFolder := teststructure.CopyTerraformFolderToTemp(t, "..", "tf/public-ec2-instance") + e2e := NewE2ETest(t) // At the end of the test, run `terraform destroy` to clean up any resources that were created - defer teststructure.RunTestStage(t, "TEARDOWN", func() { - teardown(t, tmpFolder) - }) - - // Deploy the terraform infra - teststructure.RunTestStage(t, "SETUP", func() { - setup(t, tmpFolder) - }) + defer teststructure.RunTestStage(e2e.testing, "TEARDOWN", e2e.teardown) // Upload the Zarf artifacts - teststructure.RunTestStage(t, "UPLOAD", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", username), "0700") - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf-init.tar.zst", fmt.Sprintf("/home/%s/build/zarf-init.tar.zst", username), "0600") - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf-package-appliance-demo-doom.tar.zst", fmt.Sprintf("/home/%s/build/zarf-package-appliance-demo-doom.tar.zst", username), "0600") + teststructure.RunTestStage(e2e.testing, "UPLOAD", func() { + e2e.syncFileToRemoteServer("../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", e2e.username), "0700") + e2e.syncFileToRemoteServer("../../build/zarf-init.tar.zst", fmt.Sprintf("/home/%s/build/zarf-init.tar.zst", e2e.username), "0600") + e2e.syncFileToRemoteServer("../../build/zarf-package-appliance-demo-multi-games.tar.zst", fmt.Sprintf("/home/%s/build/zarf-package-appliance-demo-multi-games.tar.zst", e2e.username), "0600") }) - teststructure.RunTestStage(t, "TEST", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) + teststructure.RunTestStage(e2e.testing, "TEST", func() { + // Make sure `zarf --help` doesn't error + output, err := e2e.runSSHCommand("sudo /home/%s/build/zarf --help", e2e.username) + require.NoError(e2e.testing, err, output) - // Finally run the actual test - testGameExample(t, terraformOptions, keyPair, username) - }) -} - -func testGameExample(t *testing.T, terraformOptions *terraform.Options, keyPair *aws.Ec2Keypair, username string) { - // Run `terraform output` to get the value of an output variable - publicInstanceIP := terraform.Output(t, terraformOptions, "public_instance_ip") - - // We're going to try to SSH to the instance IP, using the Key Pair we created earlier, and the user "ubuntu", - // as we know the Instance is running an Ubuntu AMI that has such a user - publicHost := ssh.Host{ - Hostname: publicInstanceIP, - SshKeyPair: keyPair.KeyPair, - SshUserName: username, - } + // run `zarf init` + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && ./zarf init --confirm --components k3s'", e2e.username) + require.NoError(e2e.testing, err, output) - // Make sure `zarf --help` doesn't error - output, err := ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo /home/%s/build/zarf --help", username)) - require.NoError(t, err, output) + // Deploy the game + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && ./zarf package deploy zarf-package-appliance-demo-multi-games.tar.zst --confirm'", e2e.username) + require.NoError(e2e.testing, err, output) - // run `zarf init` - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf init --confirm --components management --host 127.0.0.1'", username)) - require.NoError(t, err, output) + // Establish the port-forward into the game service; give the service a few seconds to come up since this is not a command we can retry + time.Sleep(5 * time.Second) + output, err = e2e.runSSHCommand("sudo bash -c '(/home/%s/build/zarf connect doom --local-port 22333 &> /dev/nul &)'", e2e.username) + require.NoError(e2e.testing, err, output) - // Wait until the Docker registry is ready - output, err = ssh.CheckSshCommandE(t, publicHost, "timeout 300 bash -c 'while [[ \"$(curl -sfSL --retry 15 --retry-connrefused --retry-delay 5 -o /dev/null -w \"%{http_code}\" \"https://127.0.0.1/v2/\")\" != \"401\" ]]; do sleep 1; done' || false") - require.NoError(t, err, output) + // Right now we're just checking that `curl` returns 0. It can be enhanced by scraping the HTML that gets returned or something. + output, err = e2e.runSSHCommand("bash -c '[[ $(curl -sfSL --retry 15 --retry-connrefused --retry-delay 5 -o /dev/null -w \"%%{http_code}\" 'http://127.0.0.1:22333?doom') == 200 ]]'") + require.NoError(e2e.testing, err, output) - // Deploy the game - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf package deploy zarf-package-appliance-demo-doom.tar.zst --confirm'", username)) - require.NoError(t, err, output) - - // Wait for the game to be live. Right now we're just checking that `curl` returns 0. It can be enhanced by scraping the HTML that gets returned or something. - output, err = ssh.CheckSshCommandE(t, publicHost, "timeout 300 bash -c 'while [[ \"$(curl -sfSL --retry 15 --retry-connrefused --retry-delay 5 -o /dev/null -w \"%{http_code}\" \"https://127.0.0.1\")\" != \"200\" ]]; do sleep 1; done' || false") - require.NoError(t, err, output) + // Run `zarf destroy` to make sure that works correctly + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && ./zarf destroy --confirm'", e2e.username) + require.NoError(e2e.testing, err, output) + }) - // Run `zarf destroy` to make sure that works correctly - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf destroy --confirm'", username)) - require.NoError(t, err, output) } diff --git a/test/e2e/e2e_general_cli_test.go b/test/e2e/e2e_general_cli_test.go index bd2cc900d2..54b80d209d 100644 --- a/test/e2e/e2e_general_cli_test.go +++ b/test/e2e/e2e_general_cli_test.go @@ -2,112 +2,77 @@ package test import ( "fmt" - "strings" "testing" - "github.com/gruntwork-io/terratest/modules/aws" - "github.com/gruntwork-io/terratest/modules/ssh" - "github.com/gruntwork-io/terratest/modules/terraform" teststructure "github.com/gruntwork-io/terratest/modules/test-structure" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGeneralCli(t *testing.T) { - t.Parallel() - // Our SSH username, will change based on which AMI we use - username := "ubuntu" - - // Copy the terraform folder to a temp directory so we can run multiple tests in parallel - tmpFolder := teststructure.CopyTerraformFolderToTemp(t, "..", "tf/public-ec2-instance") + e2e := NewE2ETest(t) // At the end of the test, run `terraform destroy` to clean up any resources that were created - defer teststructure.RunTestStage(t, "TEARDOWN", func() { - teardown(t, tmpFolder) - }) - - // Deploy the terraform infra - teststructure.RunTestStage(t, "SETUP", func() { - setup(t, tmpFolder) - }) + defer teststructure.RunTestStage(e2e.testing, "TEARDOWN", e2e.teardown) // Upload the Zarf artifacts - teststructure.RunTestStage(t, "UPLOAD", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", username), "0700") + teststructure.RunTestStage(e2e.testing, "UPLOAD", func() { + e2e.syncFileToRemoteServer("../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", e2e.username), "0700") }) - teststructure.RunTestStage(t, "TEST", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - - // Finally run the actual test - testGeneralCliStuff(t, terraformOptions, keyPair, username) + teststructure.RunTestStage(e2e.testing, "TEST", func() { + // Test `zarf prepare sha256sum` for a local asset + expectedShasum := "61b50898f982d015ed87093ba822de0fe011cec6dd67db39f99d8c56391a6109\n" + output, err := e2e.runSSHCommand("cd /home/%s/build && echo 'random test data 🦄' > shasum-test-file", e2e.username) + require.NoError(e2e.testing, err, output) + + output, err = e2e.runSSHCommand("cd /home/%s/build && ./zarf prepare sha256sum shasum-test-file 2> /dev/null", e2e.username) + require.NoError(e2e.testing, err, output) + assert.Equal(e2e.testing, expectedShasum, output, "The expected SHASUM should equal the actual SHASUM") + + // Test `zarf prepare sha256sum` for a remote asset + expectedShasum = "c3cdea0573ba5a058ec090b5d2683bf398e8b1614c37ec81136ed03b78167617\n" + output, err = e2e.runSSHCommand("cd /home/%s/build && ./zarf prepare sha256sum https://zarf-public.s3-us-gov-west-1.amazonaws.com/pipelines/zarf-prepare-shasum-remote-test-file.txt 2> /dev/null", e2e.username) + require.NoError(e2e.testing, err, output) + assert.Equal(e2e.testing, expectedShasum, output, "The expected SHASUM should equal the actual SHASUM") + + // Test `zarf version` + output, err = e2e.runSSHCommand("cd /home/%s/build && ./zarf version", e2e.username) + require.NoError(e2e.testing, err, output) + assert.NotNil(e2e.testing, output) + assert.NotEqual(e2e.testing, len(output), 0, "Zarf version should not be an empty string") + assert.NotEqual(e2e.testing, string(output), "UnknownVersion", "Zarf version should not be the default value") + + // Test for expected failure when given a bad component input + output, err = e2e.runSSHCommand("cd /home/%s/build && ./zarf init --confirm --components k3s,foo,logging", e2e.username) + require.Error(e2e.testing, err, output) + + // Test for expected failure when given invalid hostnames + output, err = e2e.runSSHCommand("cd /home/%s/build && ./zarf init --confirm --host localhost", e2e.username) + require.Error(e2e.testing, err, output) + + output, err = e2e.runSSHCommand("cd /home/%s/build && ./zarf pki regenerate --host zarf@server", e2e.username) + require.Error(e2e.testing, err, output) + output, err = e2e.runSSHCommand("cd /home/%s/build && ./zarf pki regenerate --host some_unique_server", e2e.username) + require.Error(e2e.testing, err, output) + + // Test that `zarf package deploy` doesn't die when given a URL + // NOTE: Temporarily commenting this out because this seems out of scope for a general cli test. Having this included also means we would have to fully standup a `zarf init` command. + // TODO: Move this to it's own e2e test. + // output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf package deploy https://zarf-examples.s3.amazonaws.com/zarf-package-appliance-demo-doom.tar.zst --confirm --insecure'", username)) + // require.NoError(t, err, output) + // output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf package deploy https://zarf-examples.s3.amazonaws.com/zarf-package-appliance-demo-doom.tar.zst --confirm --shasum e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'", username)) + // require.NoError(t, err, output) + + // Test that `zarf package deploy` gives an error if deploying a remote package without the --insecure or --shasum flags + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && ./zarf package deploy https://zarf-examples.s3.amazonaws.com/zarf-package-appliance-demo-doom-20210125.tar.zst --confirm'", e2e.username) + require.Error(e2e.testing, err, output) + + // Test that changing the log level actually applies the requested level + output, _ = e2e.runSSHCommand("cd /home/%s/build && ./zarf version --log-level warn 1> /dev/null", e2e.username) + expectedOutString := "Log level set to warn" + require.Contains(e2e.testing, output, expectedOutString, "The log level should be changed to 'warn'") }) -} -func testGeneralCliStuff(t *testing.T, terraformOptions *terraform.Options, keyPair *aws.Ec2Keypair, username string) { - // Run `terraform output` to get the value of an output variable - publicInstanceIP := terraform.Output(t, terraformOptions, "public_instance_ip") - - // We're going to try to SSH to the instance IP, using the Key Pair we created earlier, and the user "ubuntu", - // as we know the Instance is running an Ubuntu AMI that has such a user - publicHost := ssh.Host{ - Hostname: publicInstanceIP, - SshKeyPair: keyPair.KeyPair, - SshUserName: username, - } - - // Test `zarf prepare sha256sum` for a local asset - expectedShasum := "61b50898f982d015ed87093ba822de0fe011cec6dd67db39f99d8c56391a6109\n" - output, err := ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("cd /home/%s/build && echo 'random test data 🦄' > shasum-test-file", username)) - require.NoError(t, err, output) - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("cd /home/%s/build && ./zarf prepare sha256sum shasum-test-file 2> /dev/null", username)) - require.NoError(t, err, output) - assert.Equal(t, expectedShasum, output, "The expected SHASUM should equal the actual SHASUM") - - // Test `zarf prepare sha256sum` for a remote asset - expectedShasum = "c3cdea0573ba5a058ec090b5d2683bf398e8b1614c37ec81136ed03b78167617\n" - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("cd /home/%s/build && ./zarf prepare sha256sum https://zarf-public.s3-us-gov-west-1.amazonaws.com/pipelines/zarf-prepare-shasum-remote-test-file.txt 2> /dev/null", username)) - require.NoError(t, err, output) - assert.Equal(t, expectedShasum, output, "The expected SHASUM should equal the actual SHASUM") - - // Test `zarf version` - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("cd /home/%s/build && ./zarf version", username)) - require.NoError(t, err, output) - assert.NotNil(t, output) - assert.NotEqual(t, len(output), 0, "Zarf version should not be an empty string") - assert.NotEqual(t, string(output), "UnknownVersion", "Zarf version should not be the default value") - - // Test for expected failure when given a bad component input - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("cd /home/%s/build && ./zarf init --confirm --host 127.0.0.1 --components management,foo,logging", username)) - require.Error(t, err, output) - - // Test for expected failure when given invalid hostnames - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("cd /home/%s/build && ./zarf init --confirm --host localhost", username)) - require.Error(t, err, output) - - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("cd /home/%s/build && ./zarf pki regenerate --host zarf@server", username)) - require.Error(t, err, output) - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("cd /home/%s/build && ./zarf pki regenerate --host some_unique_server", username)) - require.Error(t, err, output) - - // Test that `zarf package deploy` doesn't die when given a URL - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf package deploy https://zarf-examples.s3.amazonaws.com/zarf-package-appliance-demo-doom.tar.zst --confirm --insecure'", username)) - require.NoError(t, err, output) - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf package deploy https://zarf-examples.s3.amazonaws.com/zarf-package-appliance-demo-doom.tar.zst --confirm --shasum e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'", username)) - require.NoError(t, err, output) - - // Test that `zarf package deploy` gives an error if deploying a remote package without the --insecure or --shasum flags - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf package deploy https://zarf-examples.s3.amazonaws.com/zarf-package-appliance-demo-doom.tar.zst --confirm'", username)) - require.Error(t, err, output) - - // Test that changing the log level actually applies the requested level - output, _ = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("cd /home/%s/build && ./zarf version --log-level warn 2> /dev/null", username)) - expectedOutString := "The log level has been changed to: warning" - logLevelOutput := strings.Split(output, "\n")[0] - require.Equal(t, expectedOutString, logLevelOutput, "The log level should be changed to 'warn'") } diff --git a/test/e2e/e2e_git_based_helm_chart_test.go b/test/e2e/e2e_git_based_helm_chart_test.go deleted file mode 100644 index bc5f92abc2..0000000000 --- a/test/e2e/e2e_git_based_helm_chart_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package test - -import ( - "fmt" - "github.com/gruntwork-io/terratest/modules/aws" - "github.com/gruntwork-io/terratest/modules/ssh" - "github.com/gruntwork-io/terratest/modules/terraform" - teststructure "github.com/gruntwork-io/terratest/modules/test-structure" - "github.com/stretchr/testify/require" - "testing" -) - -func TestGitBasedHelmChart(t *testing.T) { - t.Parallel() - // Our SSH username, will change based on which AMI we use - username := "ubuntu" - - // Copy the terraform folder to a temp directory so we can run multiple tests in parallel - tmpFolder := teststructure.CopyTerraformFolderToTemp(t, "..", "tf/public-ec2-instance") - - // At the end of the test, run `terraform destroy` to clean up any resources that were created - defer teststructure.RunTestStage(t, "TEARDOWN", func() { - teardown(t, tmpFolder) - }) - - // Deploy the terraform infra - teststructure.RunTestStage(t, "SETUP", func() { - setup(t, tmpFolder) - }) - - // Upload the Zarf artifacts - teststructure.RunTestStage(t, "UPLOAD", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", username), "0700") - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf-init.tar.zst", fmt.Sprintf("/home/%s/build/zarf-init.tar.zst", username), "0600") - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf-package-big-bang-single-package-demo.tar.zst", fmt.Sprintf("/home/%s/build/zarf-package-big-bang-single-package-demo.tar.zst", username), "0600") - }) - - teststructure.RunTestStage(t, "TEST", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - - // Finally run the actual test - runGitBasedCliTest(t, terraformOptions, keyPair, username) - }) -} - -func runGitBasedCliTest(t *testing.T, terraformOptions *terraform.Options, keyPair *aws.Ec2Keypair, username string) { - // Run `terraform output` to get the value of an output variable - publicInstanceIP := terraform.Output(t, terraformOptions, "public_instance_ip") - - // We're going to try to SSH to the instance IP, using the Key Pair we created earlier, and the user "ubuntu", - // as we know the Instance is running an Ubuntu AMI that has such a user - publicHost := ssh.Host{ - Hostname: publicInstanceIP, - SshKeyPair: keyPair.KeyPair, - SshUserName: username, - } - - // run `zarf init` - output, err := ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf init --confirm --components management --host 127.0.0.1'", username)) - require.NoError(t, err, output) - - // Wait until the Docker registry is ready - output, err = ssh.CheckSshCommandE(t, publicHost, "timeout 300 bash -c 'while [[ \"$(curl -sfSL --retry 15 --retry-connrefused --retry-delay 5 -o /dev/null -w \"%{http_code}\" \"https://127.0.0.1/v2/\")\" != \"401\" ]]; do sleep 1; done' || false") - require.NoError(t, err, output) - - // Deploy the single-big-bang-package example - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf package deploy zarf-package-big-bang-single-package-demo.tar.zst --confirm'", username)) - require.NoError(t, err, output) - - // Wait until the deployment is ready - output, err = ssh.CheckSshCommandE(t, publicHost, `timeout 300 sudo bash -c 'while [ "$(/usr/local/bin/kubectl get pods -n twistlock -l app=twistlock-console --field-selector=status.phase=Running -o json | jq -r '"'"'.items | length'"'"')" -lt "1" ]; do sleep 1; done' || false`) - require.NoError(t, err, output) -} diff --git a/test/e2e/e2e_gitea_and_grafana_test.go b/test/e2e/e2e_gitea_and_grafana_test.go index 4e3cc718b1..ab77305ff8 100644 --- a/test/e2e/e2e_gitea_and_grafana_test.go +++ b/test/e2e/e2e_gitea_and_grafana_test.go @@ -2,72 +2,44 @@ package test import ( "fmt" - "github.com/gruntwork-io/terratest/modules/aws" - "github.com/gruntwork-io/terratest/modules/ssh" - "github.com/gruntwork-io/terratest/modules/terraform" + "testing" + "time" + teststructure "github.com/gruntwork-io/terratest/modules/test-structure" "github.com/stretchr/testify/require" - "testing" ) func TestGiteaAndGrafana(t *testing.T) { - t.Parallel() - - // Our SSH username, will change based on which AMI we use - username := "ubuntu" - - // Copy the terraform folder to a temp directory so we can run multiple tests in parallel - tmpFolder := teststructure.CopyTerraformFolderToTemp(t, "..", "tf/public-ec2-instance") + e2e := NewE2ETest(t) // At the end of the test, run `terraform destroy` to clean up any resources that were created - defer teststructure.RunTestStage(t, "TEARDOWN", func() { - teardown(t, tmpFolder) - }) - - // Deploy the terraform infra - teststructure.RunTestStage(t, "SETUP", func() { - setup(t, tmpFolder) - }) + defer teststructure.RunTestStage(e2e.testing, "TEARDOWN", e2e.teardown) // Upload the Zarf artifacts - teststructure.RunTestStage(t, "UPLOAD", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", username), "0700") - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf-init.tar.zst", fmt.Sprintf("/home/%s/build/zarf-init.tar.zst", username), "0600") + teststructure.RunTestStage(e2e.testing, "UPLOAD", func() { + e2e.syncFileToRemoteServer("../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", e2e.username), "0700") + e2e.syncFileToRemoteServer("../../build/zarf-init.tar.zst", fmt.Sprintf("/home/%s/build/zarf-init.tar.zst", e2e.username), "0600") }) - teststructure.RunTestStage(t, "TEST", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - - // Finally run the actual test - testGiteaAndGrafana(t, terraformOptions, keyPair, username) - }) -} + teststructure.RunTestStage(e2e.testing, "TEST", func() { + // run `zarf init` + output, err := e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && ./zarf init --confirm --components k3s,logging,gitops-service'", e2e.username) + require.NoError(e2e.testing, err, output) -func testGiteaAndGrafana(t *testing.T, terraformOptions *terraform.Options, keyPair *aws.Ec2Keypair, username string) { - // Run `terraform output` to get the value of an output variable - publicInstanceIP := terraform.Output(t, terraformOptions, "public_instance_ip") + // Establish the port-forward into the gitea service; give the service a few seconds to come up since this is not a command we can retry + time.Sleep(15 * time.Second) + _, _ = e2e.runSSHCommand("sudo bash -c '(/home/%s/build/zarf connect git &> /dev/nul &)'", e2e.username) - // We're going to try to SSH to the instance IP, using the Key Pair we created earlier, and the user "ubuntu", - // as we know the Instance is running an Ubuntu AMI that has such a user - publicHost := ssh.Host{ - Hostname: publicInstanceIP, - SshKeyPair: keyPair.KeyPair, - SshUserName: username, - } + // Make sure Gitea comes up cleanly + output, err = e2e.runSSHCommand(`bash -c '[[ $(curl -sfSL -o /dev/null -w '%%{http_code}' 'http://127.0.0.1:45003/explore/repos') == 200 ]]'`) + require.NoError(e2e.testing, err, output) - // run `zarf init` - output, err := ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf init --confirm --components management,logging,gitops-service --host 127.0.0.1'", username)) - require.NoError(t, err, output) + // Establish the port-forward into the logging service + _, _ = e2e.runSSHCommand("sudo bash -c '(/home/%s/build/zarf connect logging &> /dev/nul &)'", e2e.username) - // Make sure Gitea comes up cleanly - output, err = ssh.CheckSshCommandE(t, publicHost, "timeout 300 bash -c 'while [[ \"$(curl -sfSL --retry 15 --retry-connrefused --retry-delay 5 -o /dev/null -w \"%{http_code}\" \"https://127.0.0.1/api/v1/user\")\" != \"401\" ]]; do sleep 1; done' || false") - require.NoError(t, err, output) + // // Make sure Grafana comes up cleanly + output, err = e2e.runSSHCommand(`bash -c '[[ $(curl -sfSL -o /dev/null -w '%%{http_code}' 'http://127.0.0.1:45002/monitor/login') == 200 ]]'`) + require.NoError(e2e.testing, err, output) + }) - // Make sure Grafana comes up cleanly - output, err = ssh.CheckSshCommandE(t, publicHost, "timeout 300 bash -c 'while [[ \"$(curl -sfSL --retry 15 --retry-connrefused --retry-delay 5 -o /dev/null -w \"%{http_code}\" \"https://127.0.0.1/monitor/api/org\")\" != \"401\" ]]; do sleep 1; done' || false") - require.NoError(t, err, output) } diff --git a/test/e2e/e2e_gitops_example_test.go b/test/e2e/e2e_gitops_example_test.go index 70da9b09c5..f36213f6ba 100644 --- a/test/e2e/e2e_gitops_example_test.go +++ b/test/e2e/e2e_gitops_example_test.go @@ -2,102 +2,64 @@ package test import ( "fmt" - "github.com/gruntwork-io/terratest/modules/aws" - "github.com/gruntwork-io/terratest/modules/ssh" - "github.com/gruntwork-io/terratest/modules/terraform" + "testing" + teststructure "github.com/gruntwork-io/terratest/modules/test-structure" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" ) func TestGitopsExample(t *testing.T) { - t.Parallel() - - // Our SSH username, will change based on which AMI we use - username := "ubuntu" - - // Copy the terraform folder to a temp directory so we can run multiple tests in parallel - tmpFolder := teststructure.CopyTerraformFolderToTemp(t, "..", "tf/public-ec2-instance") + e2e := NewE2ETest(t) // At the end of the test, run `terraform destroy` to clean up any resources that were created - defer teststructure.RunTestStage(t, "TEARDOWN", func() { - teardown(t, tmpFolder) - }) - - // Deploy the terraform infra - teststructure.RunTestStage(t, "SETUP", func() { - setup(t, tmpFolder) - }) + defer teststructure.RunTestStage(e2e.testing, "TEARDOWN", e2e.teardown) // Upload the Zarf artifacts - teststructure.RunTestStage(t, "UPLOAD", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", username), "0700") - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf-init.tar.zst", fmt.Sprintf("/home/%s/build/zarf-init.tar.zst", username), "0600") - syncFileToRemoteServer(t, terraformOptions, keyPair, username, "../../build/zarf-package-gitops-service-data.tar.zst", fmt.Sprintf("/home/%s/build/zarf-package-gitops-service-data.tar.zst", username), "0600") + teststructure.RunTestStage(e2e.testing, "UPLOAD", func() { + e2e.syncFileToRemoteServer("../../build/zarf", fmt.Sprintf("/home/%s/build/zarf", e2e.username), "0700") + e2e.syncFileToRemoteServer("../../build/zarf-init.tar.zst", fmt.Sprintf("/home/%s/build/zarf-init.tar.zst", e2e.username), "0600") + e2e.syncFileToRemoteServer("../../build/zarf-package-gitops-service-data.tar.zst", fmt.Sprintf("/home/%s/build/zarf-package-gitops-service-data.tar.zst", e2e.username), "0600") }) teststructure.RunTestStage(t, "TEST", func() { - terraformOptions := teststructure.LoadTerraformOptions(t, tmpFolder) - keyPair := teststructure.LoadEc2KeyPair(t, tmpFolder) - - // Finally run the actual test - testGitopsExample(t, terraformOptions, keyPair, username) + // run `zarf init` + output, err := e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && ./zarf init --confirm --components k3s,logging,gitops-service --host 127.0.0.1'", e2e.username) + require.NoError(t, err, output) + + // Deploy the gitops example + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && ./zarf package deploy zarf-package-gitops-service-data.tar.zst --confirm'", e2e.username) + require.NoError(t, err, output) + + // Create a tunnel to the git resources + output, err = e2e.runSSHCommand("sudo bash -c '(/home/%s/build/zarf connect git &> /dev/nul &)'", e2e.username) + require.NoError(t, err, output) + + // Check for full git repo mirror(foo.git) from https://github.com/stefanprodan/podinfo.git + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && git clone http://zarf-git-user:$(./zarf tools get-admin-password)@127.0.0.1:45003/zarf-git-user/mirror__github.com__stefanprodan__podinfo.git'", e2e.username) + require.NoError(t, err, output) + + // Check for tagged git repo mirror (foo.git@1.2.3) from https://github.com/defenseunicorns/zarf.git@v0.12.0 + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build && git clone http://zarf-git-user:$(./zarf tools get-admin-password)@127.0.0.1:45003/zarf-git-user/mirror__github.com__defenseunicorns__zarf.git'", e2e.username) + require.NoError(t, err, output) + + // Check for correct tag + expectedTag := "v0.12.0\n" + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build/mirror__github.com__defenseunicorns__zarf && git tag'", e2e.username) + require.NoError(t, err, output) + assert.Equal(t, expectedTag, output, "Expected tag should match output") + + // Check for correct commits + expectedCommits := "4fb0f14\ncd45237\n9ac3338" + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build/mirror__github.com__defenseunicorns__zarf && git log -3 --oneline --pretty=format:\"%%h\"'", e2e.username) + require.NoError(t, err, output) + assert.Equal(t, expectedCommits, output, "Expected commits should match output") + + // Check for correct branches + expectedBranch := "* master\n" + output, err = e2e.runSSHCommand("sudo bash -c 'cd /home/%s/build/mirror__github.com__stefanprodan__podinfo && git branch --list'", e2e.username) + require.NoError(t, err, output) + assert.Equal(t, expectedBranch, output, "Expected Branch should match output") }) -} - -func testGitopsExample(t *testing.T, terraformOptions *terraform.Options, keyPair *aws.Ec2Keypair, username string) { - // Run `terraform output` to get the value of an output variable - publicInstanceIP := terraform.Output(t, terraformOptions, "public_instance_ip") - - // We're going to try to SSH to the instance IP, using the Key Pair we created earlier, and the user "ubuntu", - // as we know the Instance is running an Ubuntu AMI that has such a user - publicHost := ssh.Host{ - Hostname: publicInstanceIP, - SshKeyPair: keyPair.KeyPair, - SshUserName: username, - } - - // run `zarf init` - output, err := ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf init --confirm --components management,logging,gitops-service --host 127.0.0.1'", username)) - require.NoError(t, err, output) - - // Make sure Gitea comes up cleanly - output, err = ssh.CheckSshCommandE(t, publicHost, "timeout 300 bash -c 'while [[ \"$(curl -sfSL --retry 15 --retry-connrefused --retry-delay 5 -o /dev/null -w \"%{http_code}\" \"https://127.0.0.1/api/v1/user\")\" != \"401\" ]]; do sleep 1; done' || false") - require.NoError(t, err, output) - - // Deploy the gitops example - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && ./zarf package deploy zarf-package-gitops-service-data.tar.zst --confirm'", username)) - require.NoError(t, err, output) - - // Check for full git repo mirror(foo.git) from https://github.com/stefanprodan/podinfo.git - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && git clone https://zarf-git-user:$(./zarf tools get-admin-password)@127.0.0.1/zarf-git-user/mirror__github.com__stefanprodan__podinfo.git'", username)) - require.NoError(t, err, output) - - // Check for tagged git repo mirror (foo.git@1.2.3) from https://github.com/defenseunicorns/zarf.git@v0.12.0 - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build && git clone https://zarf-git-user:$(./zarf tools get-admin-password)@127.0.0.1/zarf-git-user/mirror__github.com__defenseunicorns__zarf.git'", username)) - require.NoError(t, err, output) - - // Check for correct tag - expectedTag := "v0.12.0\n" - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build/mirror__github.com__defenseunicorns__zarf && git tag'", username)) - require.NoError(t, err, output) - assert.Equal(t, expectedTag, output, "Expected tag should match output") - - // Check for correct commits - expectedCommits := "4fb0f14\ncd45237\n9ac3338" - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build/mirror__github.com__defenseunicorns__zarf && git log -3 --oneline --pretty=format:\"%%h\"'", username)) - require.NoError(t, err, output) - assert.Equal(t, expectedCommits, output, "Expected commits should match output") - - // Check for correct branches - expectedBranch := "* master\n" - output, err = ssh.CheckSshCommandE(t, publicHost, fmt.Sprintf("sudo bash -c 'cd /home/%s/build/mirror__github.com__stefanprodan__podinfo && git branch --list'", username)) - require.NoError(t, err, output) - assert.Equal(t, expectedBranch, output, "Expected Branch should match output") - } diff --git a/zarf.schema.json b/zarf.schema.json index 84a7ffcace..479f232e0d 100644 --- a/zarf.schema.json +++ b/zarf.schema.json @@ -6,6 +6,7 @@ "required": [ "terminal", "user", + "architecture", "timestamp", "string" ], @@ -16,6 +17,9 @@ "user": { "type": "string" }, + "architecture": { + "type": "string" + }, "timestamp": { "type": "string" }, @@ -30,7 +34,8 @@ "required": [ "name", "url", - "version" + "version", + "namespace" ], "properties": { "name": { @@ -41,6 +46,18 @@ }, "version": { "type": "string" + }, + "namespace": { + "type": "string" + }, + "valuesFiles": { + "items": { + "type": "string" + }, + "type": "array" + }, + "gitPath": { + "type": "string" } }, "additionalProperties": false, @@ -63,6 +80,9 @@ "required": { "type": "boolean" }, + "secretName": { + "type": "string" + }, "files": { "items": { "$schema": "http://json-schema.org/draft-04/schema#", @@ -70,19 +90,23 @@ }, "type": "array" }, - "manifests": { - "type": "string" - }, - "images": { + "charts": { "items": { - "type": "string" + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/ZarfChart" }, "type": "array" }, - "charts": { + "manifests": { "items": { "$schema": "http://json-schema.org/draft-04/schema#", - "$ref": "#/definitions/ZarfChart" + "$ref": "#/definitions/ZarfManifest" + }, + "type": "array" + }, + "images": { + "items": { + "type": "string" }, "type": "array" }, @@ -184,9 +208,33 @@ "type": "string" }, "type": "array" + } + }, + "additionalProperties": false, + "type": "object" + }, + "ZarfManifest": { + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" }, - "template": { - "type": "boolean" + "kustomizations": { + "items": { + "type": "string" + }, + "type": "array" } }, "additionalProperties": false, @@ -203,8 +251,17 @@ "version": { "type": "string" }, + "url": { + "type": "string" + }, + "image": { + "type": "string" + }, "uncompressed": { "type": "boolean" + }, + "architecture": { + "type": "string" } }, "additionalProperties": false, @@ -236,6 +293,12 @@ "$ref": "#/definitions/ZarfComponent" }, "type": "array" + }, + "seed": { + "items": { + "type": "string" + }, + "type": "array" } }, "additionalProperties": false, diff --git a/zarf.yaml b/zarf.yaml index e299e7a708..a215e42067 100644 --- a/zarf.yaml +++ b/zarf.yaml @@ -1,20 +1,33 @@ kind: ZarfInitConfig +metadata: + name: Zarf Official Init Package + description: "Used to establish a new Zarf cluster" + +seed: + - library/registry:2.7.1 components: - name: k3s description: > + *** REQUIRES ROOT *** Install K3s, certified Kubernetes distribution built for IoT & Edge computing. - K3s provides the cluster need for Zarf running in Appliance MOde as well as can + 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. - required: true scripts: retry: true + before: + # 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 + - "[ -e /etc/redhat-release ] && systemctl disable firewalld --now || echo ''" after: # Configure K3s systemd service - "systemctl daemon-reload" - "systemctl enable --now k3s" # Wait for the K3s node to come up - "/usr/local/bin/kubectl get nodes" + # Make sure things are really ready in k8s + - "/usr/local/bin/kubectl wait --for=condition=available deployment/coredns -n kube-system" files: # Include the actual K3s binary - source: https://github.com/k3s-io/k3s/releases/download/v1.21.6+k3s1/k3s @@ -39,76 +52,60 @@ components: target: /etc/systemd/system/k3s.service symlinks: - /etc/systemd/system/multi-user.target.wants/k3s.service - # Containerd mirroring configuration with zarf string injection - - source: assets/misc/registries.yaml - target: "/etc/rancher/k3s/registries.yaml" - template: true # Mock file for creating the kube config symlink - source: assets/misc/empty-file target: /etc/rancher/k3s/k3s.yaml symlinks: - /root/.kube/config - - name: management - description: "Add the K9s terminal-based K8s UI for cluster management" - default: true - files: - # The zarf binary is hosted on govcloud since the release on https://github.com/derailed/k9s/ is a tarball - - source: https://zarf-public.s3-us-gov-west-1.amazonaws.com/k9s_Linux_x86_64_v0_24_11 - shasum: 18a5a33bbf58cb228e56a03380dcb6b9bb8624acab4ff63deb7364dc15d3c03f - target: /usr/local/bin/k9s - executable: true - # Simple theme file to make K9s colors similar to Zarfs UI colors - - source: assets/misc/k9s-theme.yaml - target: /root/.k9s/skin.yml - - - name: traefik-ingress - description: "Install the Traefik ingress (usually needed for appliance mode)" + - name: container-registry-seed required: true - scripts: - retry: true - after: - - "/usr/local/bin/kubectl get middleware" - manifests: assets/manifests/traefik charts: - - name: traefik - url: https://helm.traefik.io/traefik - version: 9.18.2 + - name: docker-registry + url: https://github.com/defenseunicorns/docker-registry.helm.git + version: 2.0.0 + namespace: zarf + valuesFiles: + - assets/charts/registry-values.yaml + - assets/charts/registry-values-seed.yaml - name: container-registry required: true - scripts: - retry: true - after: - - "./zarf tools registry catalog $ZARF_TARGET_ENDPOINT" - manifests: assets/manifests/registry - images: - - registry1.dso.mil/ironbank/opensource/docker/registry-v2:2.7.1 + manifests: + - name: kep-1775-registry-annotation + files: + - assets/manifests/registry/configmap.yaml charts: - name: docker-registry - url: https://helm.twun.io - version: 1.10.1 + url: https://github.com/defenseunicorns/docker-registry.helm.git + version: 2.0.0 + namespace: zarf + valuesFiles: + - assets/charts/registry-values.yaml - name: logging description: "Add Promtail, Grafana and Loki (PGL) to this cluster for log monitoring." - default: true - manifests: assets/manifests/logging images: - - grafana/loki:2.2.0 + - grafana/grafana:8.1.6 + - grafana/loki:2.4.1 - grafana/promtail:2.1.0 - - grafana/grafana:7.5.0 - - kiwigrid/k8s-sidecar:0.1.209 + - quay.io/kiwigrid/k8s-sidecar:1.12.3 charts: - name: loki-stack url: https://grafana.github.io/helm-charts - version: 2.4.1 + version: 2.5.1 + namespace: zarf + valuesFiles: + - assets/charts/pgl-values.yaml - name: gitops-service - description: "Add Gitea for serving gitops-based clusters in an airgap" - manifests: assets/manifests/gitops + description: "Add Registry and Gitea for serving gitops-based clusters in an airgap" images: - gitea/gitea:1.13.7 charts: - name: gitea url: https://dl.gitea.io/charts version: 2.2.5 + namespace: zarf + valuesFiles: + - assets/charts/gitea-values.yaml