Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb96bca6aa | ||
|
|
c18d0b9217 | ||
|
|
d1970185b9 | ||
|
|
794d3ee2f5 | ||
|
|
daeef98dfb | ||
|
|
4146df1f02 | ||
|
|
78f4dfa48d | ||
|
|
8b68d7d7e2 | ||
|
|
ba1bba17ad | ||
|
|
70f2b5028f | ||
|
|
4f4f0d70a4 | ||
|
|
9607b07e65 | ||
|
|
3570fab0f9 | ||
|
|
dd0f17d7a5 | ||
|
|
2290880389 | ||
|
|
351d287d88 | ||
|
|
eba6706f15 | ||
|
|
7f425efa6b | ||
|
|
d40685ab62 | ||
|
|
f795e3eeb8 | ||
|
|
d3944c0204 | ||
|
|
4086ab15fa | ||
|
|
446a367094 | ||
|
|
bd250c9871 | ||
|
|
91b3788362 | ||
|
|
658dbb9ea8 | ||
|
|
1257b9cbc8 | ||
|
|
0ae39d5a0a | ||
|
|
d8911e0c77 | ||
|
|
1be006a45f | ||
|
|
b95e75ddb4 | ||
|
|
15a5f75fe7 | ||
|
|
b01e27f50f | ||
|
|
b1a9583262 | ||
|
|
dd5e6377f8 | ||
|
|
fcb73554c9 | ||
|
|
5c4b3d1080 | ||
|
|
c98cd10621 | ||
|
|
e4af8ddcc8 | ||
|
|
b5ce02b8cf | ||
|
|
cd42b3fb44 | ||
|
|
6dedb767c3 | ||
|
|
4de3268f3b | ||
|
|
a72ff06d79 | ||
|
|
cf239deb3d | ||
|
|
8735229745 | ||
|
|
3f081ba2d9 | ||
|
|
3f613341cb | ||
|
|
4305b8a77d | ||
|
|
fab91d44c3 | ||
|
|
34484734d8 | ||
|
|
10c58bb007 | ||
|
|
1c1fd62a91 | ||
|
|
637ac1e3fe | ||
|
|
f52387e849 | ||
|
|
9efc4986f9 | ||
|
|
fdc366ec1c | ||
|
|
d455db444c | ||
|
|
17418c9858 | ||
|
|
5da7eb7d0d | ||
|
|
9da977ee7a | ||
|
|
8e4b2ead9b | ||
|
|
fd364828a1 | ||
|
|
afa58d8c08 | ||
|
|
179062876e | ||
|
|
a796f3609f | ||
|
|
b7c6db74d2 | ||
|
|
4f7b040405 | ||
|
|
34ca29830e | ||
|
|
78f1b634fa | ||
|
|
044bc64ad9 | ||
|
|
091f439498 | ||
|
|
a17b0a1ce0 | ||
|
|
354cd5e177 | ||
|
|
4e8f1221f7 | ||
|
|
6b179aa7d9 | ||
|
|
f748114dfa | ||
|
|
5de83f015a | ||
|
|
a6620e478a | ||
|
|
c7fcffdd8e | ||
|
|
160f59a984 | ||
|
|
d38d487c2a | ||
|
|
db28907543 |
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,46 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve Flux v2
|
|
||||||
title: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Find out more about your support options and getting help at
|
|
||||||
|
|
||||||
https://fluxcd.io/support/
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
### Describe the bug
|
|
||||||
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
### To Reproduce
|
|
||||||
|
|
||||||
Steps to reproduce the behaviour:
|
|
||||||
|
|
||||||
1. Provide Flux install instructions
|
|
||||||
2. Provide a GitHub repository with Kubernetes manifests
|
|
||||||
|
|
||||||
### Expected behavior
|
|
||||||
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
### Additional context
|
|
||||||
|
|
||||||
- Kubernetes version:
|
|
||||||
- Git provider:
|
|
||||||
- Container registry provider:
|
|
||||||
|
|
||||||
Below please provide the output of the following commands:
|
|
||||||
|
|
||||||
```cli
|
|
||||||
flux --version
|
|
||||||
flux check
|
|
||||||
kubectl -n <namespace> get all
|
|
||||||
kubectl -n <namespace> logs deploy/source-controller
|
|
||||||
kubectl -n <namespace> logs deploy/kustomize-controller
|
|
||||||
```
|
|
||||||
85
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
85
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
description: Create a report to help us improve Flux
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## Support
|
||||||
|
Find out more about your support options and getting help at: https://fluxcd.io/support/
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug
|
||||||
|
description: A clear description of what the bug is.
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: |
|
||||||
|
Steps to reproduce the problem.
|
||||||
|
placeholder: |
|
||||||
|
For example:
|
||||||
|
1. Install Flux with the additional image automation controllers
|
||||||
|
2. Run command '...'
|
||||||
|
3. See error
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Expected behavior
|
||||||
|
description: A brief description of what you expected to happen.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Screenshots and recordings
|
||||||
|
description: |
|
||||||
|
If applicable, add screenshots to help explain your problem. You can also record an asciinema session: https://asciinema.org/
|
||||||
|
- type: input
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: OS / Distro
|
||||||
|
description: The OS / distro you are executing `flux` on. If not applicable, write `N/A`.
|
||||||
|
placeholder: e.g. Windows 10, Ubuntu 20.04, Arch Linux, macOS 10.15...
|
||||||
|
- type: input
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Flux version
|
||||||
|
description: Run `flux --version` to check. If not applicable, write `N/A`.
|
||||||
|
placeholder: e.g. 0.16.1
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Flux check
|
||||||
|
description: Run `flux check` to check. If not applicable, write `N/A`.
|
||||||
|
placeholder: |
|
||||||
|
For example:
|
||||||
|
► checking prerequisites
|
||||||
|
✔ kubectl 1.21.0 >=1.18.0-0
|
||||||
|
✔ Kubernetes 1.21.1 >=1.16.0-0
|
||||||
|
► checking controllers
|
||||||
|
✔ all checks passed
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Git provider
|
||||||
|
description: If applicable, add the Git provider you are having problems with, e.g. GitHub (Enterprise), GitLab, etc.
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Container Registry provider
|
||||||
|
description: If applicable, add the Container Registry provider you are having problems with, e.g. DockerHub, GitHub Packages, Quay.io, etc.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context about the problem here. This can be logs (e.g. output from `flux logs`), environment specific caveats, etc.
|
||||||
|
- type: checkboxes
|
||||||
|
id: terms
|
||||||
|
attributes:
|
||||||
|
label: Code of Conduct
|
||||||
|
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/fluxcd/.github/blob/main/CODE_OF_CONDUCT.md)
|
||||||
|
options:
|
||||||
|
- label: I agree to follow this project's Code of Conduct
|
||||||
|
required: true
|
||||||
42
.github/runners/README.md
vendored
Normal file
42
.github/runners/README.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Flux GitHub runners
|
||||||
|
|
||||||
|
How to provision GitHub Actions self-hosted runners for Flux conformance testing.
|
||||||
|
|
||||||
|
## ARM64 Instance specs
|
||||||
|
|
||||||
|
In order to add a new runner to the GitHub Actions pool,
|
||||||
|
first create an instance on Oracle Cloud with the following configuration:
|
||||||
|
- OS: Canonical Ubuntu 20.04
|
||||||
|
- Shape: VM.Standard.A1.Flex
|
||||||
|
- OCPU Count: 2
|
||||||
|
- Memory (GB): 12
|
||||||
|
- Network Bandwidth (Gbps): 2
|
||||||
|
- Local Disk: Block Storage Only
|
||||||
|
|
||||||
|
Note that the instance image source must be **Canonical Ubuntu** instead of the default Oracle Linux.
|
||||||
|
|
||||||
|
## ARM64 Instance setup
|
||||||
|
|
||||||
|
- SSH into a newly created instance
|
||||||
|
```shell
|
||||||
|
ssh ubuntu@<instance-public-IP>
|
||||||
|
```
|
||||||
|
- Create the action runner dir
|
||||||
|
```shell
|
||||||
|
mkdir -p actions-runner && cd actions-runner
|
||||||
|
```
|
||||||
|
- Download the provisioning script
|
||||||
|
```shell
|
||||||
|
curl -sL https://raw.githubusercontent.com/fluxcd/flux2/main/.github/runners/arm64.sh > arm64.sh \
|
||||||
|
&& chmod +x ./arm64.sh
|
||||||
|
```
|
||||||
|
- Retrieve the GitHub runner token from the repository [settings page](https://github.com/fluxcd/flux2/settings/actions/runners/new?arch=arm64&os=linux)
|
||||||
|
- Run the provisioning script passing the token as the first argument
|
||||||
|
```shell
|
||||||
|
sudo ./arm64.sh <TOKEN>
|
||||||
|
```
|
||||||
|
- Reboot the instance
|
||||||
|
```shell
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
|
- Navigate to the GitHub repository [runners page](https://github.com/fluxcd/flux2/settings/actions/runners) and check the runner status
|
||||||
73
.github/runners/arm64.sh
vendored
Executable file
73
.github/runners/arm64.sh
vendored
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright 2021 The Flux authors. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# This script is meant to be run locally and in CI to validate the Kubernetes
|
||||||
|
# manifests (including Flux custom resources) before changes are merged into
|
||||||
|
# the branch synced by Flux in-cluster.
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
REPOSITORY_TOKEN=$1
|
||||||
|
REPOSITORY_URL=${2:-https://github.com/fluxcd/flux2}
|
||||||
|
|
||||||
|
KIND_VERSION=0.11.1
|
||||||
|
KUBECTL_VERSION=1.21.2
|
||||||
|
KUSTOMIZE_VERSION=4.1.3
|
||||||
|
GITHUB_RUNNER_VERSION=2.278.0
|
||||||
|
PACKAGES="apt-transport-https ca-certificates software-properties-common build-essential libssl-dev gnupg lsb-release jq"
|
||||||
|
|
||||||
|
# install prerequisites
|
||||||
|
apt-get update \
|
||||||
|
&& apt-get install -y -q ${PACKAGES} \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# install docker
|
||||||
|
curl -fsSL https://get.docker.com -o get-docker.sh \
|
||||||
|
&& chmod +x get-docker.sh
|
||||||
|
./get-docker.sh
|
||||||
|
systemctl enable docker.service
|
||||||
|
systemctl enable containerd.service
|
||||||
|
usermod -aG docker ubuntu
|
||||||
|
|
||||||
|
# install kind
|
||||||
|
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v${KIND_VERSION}/kind-linux-arm64
|
||||||
|
install -o root -g root -m 0755 kind /usr/local/bin/kind
|
||||||
|
|
||||||
|
# install kubectl
|
||||||
|
curl -LO "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/arm64/kubectl"
|
||||||
|
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
|
||||||
|
|
||||||
|
# install kustomize
|
||||||
|
curl -Lo ./kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${KUSTOMIZE_VERSION}/kustomize_v${KUSTOMIZE_VERSION}_linux_arm64.tar.gz \
|
||||||
|
&& tar -zxvf kustomize.tar.gz \
|
||||||
|
&& rm kustomize.tar.gz
|
||||||
|
install -o root -g root -m 0755 kustomize /usr/local/bin/kustomize
|
||||||
|
|
||||||
|
# download runner
|
||||||
|
curl -o actions-runner-linux-arm64.tar.gz -L https://github.com/actions/runner/releases/download/v${GITHUB_RUNNER_VERSION}/actions-runner-linux-arm64-${GITHUB_RUNNER_VERSION}.tar.gz \
|
||||||
|
&& tar xzf actions-runner-linux-arm64.tar.gz \
|
||||||
|
&& rm actions-runner-linux-arm64.tar.gz
|
||||||
|
|
||||||
|
# install runner dependencies
|
||||||
|
./bin/installdependencies.sh
|
||||||
|
|
||||||
|
# register runner with GitHub
|
||||||
|
sudo -u ubuntu ./config.sh --unattended --url ${REPOSITORY_URL} --token ${REPOSITORY_TOKEN}
|
||||||
|
|
||||||
|
# start runner
|
||||||
|
./svc.sh install
|
||||||
|
./svc.sh start
|
||||||
10
.github/workflows/bootstrap.yaml
vendored
10
.github/workflows/bootstrap.yaml
vendored
@@ -26,6 +26,9 @@ jobs:
|
|||||||
go-version: 1.16.x
|
go-version: 1.16.x
|
||||||
- name: Setup Kubernetes
|
- name: Setup Kubernetes
|
||||||
uses: engineerd/setup-kind@v0.5.0
|
uses: engineerd/setup-kind@v0.5.0
|
||||||
|
with:
|
||||||
|
version: v0.11.1
|
||||||
|
image: kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6
|
||||||
- name: Setup Kustomize
|
- name: Setup Kustomize
|
||||||
uses: fluxcd/pkg//actions/kustomize@main
|
uses: fluxcd/pkg//actions/kustomize@main
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -61,6 +64,13 @@ jobs:
|
|||||||
--team=team-z
|
--team=team-z
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||||
|
- name: libgit2
|
||||||
|
run: |
|
||||||
|
/tmp/flux create source git test-libgit2 \
|
||||||
|
--url=ssh://git@github.com/fluxcd-testing/${{ steps.vars.outputs.test_repo_name }} \
|
||||||
|
--git-implementation=libgit2 \
|
||||||
|
--secret-ref=flux-system \
|
||||||
|
--branch=main
|
||||||
- name: uninstall
|
- name: uninstall
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux uninstall -s --keep-namespace
|
/tmp/flux uninstall -s --keep-namespace
|
||||||
|
|||||||
108
.github/workflows/e2e-arm64.yaml
vendored
Normal file
108
.github/workflows/e2e-arm64.yaml
vendored
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
name: e2e-arm64
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ main, update-components ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ampere:
|
||||||
|
# Runner info
|
||||||
|
# Owner: Stefan Prodan
|
||||||
|
# Docs: https://github.com/fluxcd/flux2/tree/main/.github/runners
|
||||||
|
runs-on: [self-hosted, Linux, ARM64]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.16.x
|
||||||
|
- name: Prepare
|
||||||
|
id: prep
|
||||||
|
run: |
|
||||||
|
echo ::set-output name=CLUSTER::arm64-${GITHUB_SHA:0:7}-$(date +%s)
|
||||||
|
echo ::set-output name=CONTEXT::kind-arm64-${GITHUB_SHA:0:7}-$(date +%s)
|
||||||
|
- name: Run unit tests
|
||||||
|
run: make test
|
||||||
|
- name: Check if working tree is dirty
|
||||||
|
run: |
|
||||||
|
if [[ $(git diff --stat) != '' ]]; then
|
||||||
|
git diff
|
||||||
|
echo 'run make test and commit changes'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
go build -o /tmp/flux ./cmd/flux
|
||||||
|
- name: Setup Kubernetes Kind
|
||||||
|
run: |
|
||||||
|
kind create cluster --name ${{ steps.prep.outputs.CLUSTER }}
|
||||||
|
- name: flux check --pre
|
||||||
|
run: |
|
||||||
|
/tmp/flux check --pre \
|
||||||
|
--context ${{ steps.prep.outputs.CONTEXT }}
|
||||||
|
- name: flux install
|
||||||
|
run: |
|
||||||
|
/tmp/flux install \
|
||||||
|
--components-extra=image-reflector-controller,image-automation-controller \
|
||||||
|
--context ${{ steps.prep.outputs.CONTEXT }}
|
||||||
|
- name: flux create source git
|
||||||
|
run: |
|
||||||
|
/tmp/flux create source git podinfo-gogit \
|
||||||
|
--git-implementation=go-git \
|
||||||
|
--url https://github.com/stefanprodan/podinfo \
|
||||||
|
--tag-semver=">1.0.0" \
|
||||||
|
--context ${{ steps.prep.outputs.CONTEXT }}
|
||||||
|
/tmp/flux create source git podinfo-libgit2 \
|
||||||
|
--git-implementation=libgit2 \
|
||||||
|
--url https://github.com/stefanprodan/podinfo \
|
||||||
|
--branch="master" \
|
||||||
|
--context ${{ steps.prep.outputs.CONTEXT }}
|
||||||
|
- name: flux create kustomization
|
||||||
|
run: |
|
||||||
|
/tmp/flux create kustomization podinfo \
|
||||||
|
--source=podinfo-gogit \
|
||||||
|
--path="./deploy/overlays/dev" \
|
||||||
|
--prune=true \
|
||||||
|
--interval=5m \
|
||||||
|
--validation=client \
|
||||||
|
--health-check="Deployment/frontend.dev" \
|
||||||
|
--health-check="Deployment/backend.dev" \
|
||||||
|
--health-check-timeout=3m \
|
||||||
|
--context ${{ steps.prep.outputs.CONTEXT }}
|
||||||
|
- name: flux create tenant
|
||||||
|
run: |
|
||||||
|
/tmp/flux create tenant dev-team \
|
||||||
|
--with-namespace=apps \
|
||||||
|
--context ${{ steps.prep.outputs.CONTEXT }}
|
||||||
|
- name: flux create helmrelease
|
||||||
|
run: |
|
||||||
|
/tmp/flux -n apps create source helm podinfo \
|
||||||
|
--url https://stefanprodan.github.io/podinfo \
|
||||||
|
--context ${{ steps.prep.outputs.CONTEXT }}
|
||||||
|
|
||||||
|
/tmp/flux -n apps create hr podinfo-helm \
|
||||||
|
--source=HelmRepository/podinfo \
|
||||||
|
--chart=podinfo \
|
||||||
|
--chart-version="6.0.x" \
|
||||||
|
--service-account=dev-team \
|
||||||
|
--context ${{ steps.prep.outputs.CONTEXT }}
|
||||||
|
- name: flux get all
|
||||||
|
run: |
|
||||||
|
/tmp/flux get all --all-namespaces \
|
||||||
|
--context ${{ steps.prep.outputs.CONTEXT }}
|
||||||
|
- name: flux uninstall
|
||||||
|
run: |
|
||||||
|
/tmp/flux uninstall -s \
|
||||||
|
--context ${{ steps.prep.outputs.CONTEXT }}
|
||||||
|
- name: Debug failure
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
kubectl --context ${{ steps.prep.outputs.CONTEXT }} -n flux-system get all
|
||||||
|
kubectl --context ${{ steps.prep.outputs.CONTEXT }} -n flux-system describe pods
|
||||||
|
/tmp/flux logs --all-namespaces
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
kind delete cluster --name ${{ steps.prep.outputs.CLUSTER }}
|
||||||
13
.github/workflows/e2e.yaml
vendored
13
.github/workflows/e2e.yaml
vendored
@@ -26,8 +26,8 @@ jobs:
|
|||||||
- name: Setup Kubernetes
|
- name: Setup Kubernetes
|
||||||
uses: engineerd/setup-kind@v0.5.0
|
uses: engineerd/setup-kind@v0.5.0
|
||||||
with:
|
with:
|
||||||
version: "v0.10.0"
|
version: v0.11.1
|
||||||
image: kindest/node:v1.20.2@sha256:8f7ea6e7642c0da54f04a7ee10431549c0257315b3a634f6ef2fecaaedb19bab
|
image: kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6
|
||||||
config: .github/kind/config.yaml # disable KIND-net
|
config: .github/kind/config.yaml # disable KIND-net
|
||||||
- name: Setup Calico for network policy
|
- name: Setup Calico for network policy
|
||||||
run: |
|
run: |
|
||||||
@@ -91,6 +91,12 @@ jobs:
|
|||||||
--health-check="Deployment/frontend.dev" \
|
--health-check="Deployment/frontend.dev" \
|
||||||
--health-check="Deployment/backend.dev" \
|
--health-check="Deployment/backend.dev" \
|
||||||
--health-check-timeout=3m
|
--health-check-timeout=3m
|
||||||
|
- name: flux trace
|
||||||
|
run: |
|
||||||
|
/tmp/flux trace frontend \
|
||||||
|
--kind=deployment \
|
||||||
|
--api-version=apps/v1 \
|
||||||
|
--namespace=dev
|
||||||
- name: flux reconcile kustomization --with-source
|
- name: flux reconcile kustomization --with-source
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux reconcile kustomization podinfo --with-source
|
/tmp/flux reconcile kustomization podinfo --with-source
|
||||||
@@ -193,7 +199,7 @@ jobs:
|
|||||||
/tmp/flux create kustomization flux-system \
|
/tmp/flux create kustomization flux-system \
|
||||||
--source=flux-system \
|
--source=flux-system \
|
||||||
--path=./clusters/staging
|
--path=./clusters/staging
|
||||||
kubectl -n flux-system wait kustomization/apps --for=condition=ready --timeout=2m
|
kubectl -n flux-system wait kustomization/apps --for=condition=ready --timeout=5m
|
||||||
- name: flux check
|
- name: flux check
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux check
|
/tmp/flux check
|
||||||
@@ -205,6 +211,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
kubectl version --client --short
|
kubectl version --client --short
|
||||||
kubectl -n flux-system get all
|
kubectl -n flux-system get all
|
||||||
|
kubectl -n flux-system describe pods
|
||||||
kubectl -n flux-system get kustomizations -oyaml
|
kubectl -n flux-system get kustomizations -oyaml
|
||||||
kubectl -n flux-system logs deploy/source-controller
|
kubectl -n flux-system logs deploy/source-controller
|
||||||
kubectl -n flux-system logs deploy/kustomize-controller
|
kubectl -n flux-system logs deploy/kustomize-controller
|
||||||
|
|||||||
@@ -10,11 +10,21 @@ Usage:
|
|||||||
run: flux -v
|
run: flux -v
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that this action can only be used on GitHub **Linux AMD64** runners.
|
|
||||||
The latest stable version of the `flux` binary is downloaded from
|
The latest stable version of the `flux` binary is downloaded from
|
||||||
GitHub [releases](https://github.com/fluxcd/flux2/releases)
|
GitHub [releases](https://github.com/fluxcd/flux2/releases)
|
||||||
and placed at `/usr/local/bin/flux`.
|
and placed at `/usr/local/bin/flux`.
|
||||||
|
|
||||||
|
Note that this action can only be used on GitHub **Linux** runners.
|
||||||
|
You can change the arch (defaults to `amd64`) with:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- name: Setup Flux CLI
|
||||||
|
uses: fluxcd/flux2/action@main
|
||||||
|
with:
|
||||||
|
arch: arm64 # can be amd64, arm64 or arm
|
||||||
|
```
|
||||||
|
|
||||||
You can download a specific version with:
|
You can download a specific version with:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -8,19 +8,24 @@ inputs:
|
|||||||
version:
|
version:
|
||||||
description: "Flux version e.g. 0.8.0 (defaults to latest stable release)"
|
description: "Flux version e.g. 0.8.0 (defaults to latest stable release)"
|
||||||
required: false
|
required: false
|
||||||
|
arch:
|
||||||
|
description: "arch can be amd64, arm64 or arm"
|
||||||
|
required: true
|
||||||
|
default: "amd64"
|
||||||
runs:
|
runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- name: "Download flux binary to tmp"
|
- name: "Download flux binary to tmp"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
ARCH=${{ inputs.arch }}
|
||||||
VERSION=${{ inputs.version }}
|
VERSION=${{ inputs.version }}
|
||||||
|
|
||||||
if [ -z $VERSION ]; then
|
if [ -z $VERSION ]; then
|
||||||
VERSION=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest -sL | grep tag_name | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
|
VERSION=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest -sL | grep tag_name | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_amd64.tar.gz"
|
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_${ARCH}.tar.gz"
|
||||||
curl -sL ${BIN_URL} -o /tmp/flux.tar.gz
|
curl -sL ${BIN_URL} -o /tmp/flux.tar.gz
|
||||||
mkdir -p /tmp/flux
|
mkdir -p /tmp/flux
|
||||||
tar -C /tmp/flux/ -zxvf /tmp/flux.tar.gz
|
tar -C /tmp/flux/ -zxvf /tmp/flux.tar.gz
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ func (a alertAdapter) asClientObject() client.Object {
|
|||||||
return a.Alert
|
return a.Alert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a alertAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.Alert.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// notificationv1.Alert
|
// notificationv1.Alert
|
||||||
|
|
||||||
type alertListAdapter struct {
|
type alertListAdapter struct {
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ func (a alertProviderAdapter) asClientObject() client.Object {
|
|||||||
return a.Provider
|
return a.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a alertProviderAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.Provider.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// notificationv1.Provider
|
// notificationv1.Provider
|
||||||
|
|
||||||
type alertProviderListAdapter struct {
|
type alertProviderListAdapter struct {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ func buildEmbeddedManifestBase() (string, error) {
|
|||||||
if !isEmbeddedVersion(bootstrapArgs.version) {
|
if !isEmbeddedVersion(bootstrapArgs.version) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
tmpBaseDir, err := ioutil.TempDir("", "flux-manifests-")
|
tmpBaseDir, err := os.MkdirTemp("", "flux-manifests-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -69,6 +68,7 @@ type gitFlags struct {
|
|||||||
path flags.SafeRelativePath
|
path flags.SafeRelativePath
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
|
silent bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var gitArgs gitFlags
|
var gitArgs gitFlags
|
||||||
@@ -79,6 +79,7 @@ func init() {
|
|||||||
bootstrapGitCmd.Flags().Var(&gitArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
bootstrapGitCmd.Flags().Var(&gitArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
bootstrapGitCmd.Flags().StringVarP(&gitArgs.username, "username", "u", "git", "basic authentication username")
|
bootstrapGitCmd.Flags().StringVarP(&gitArgs.username, "username", "u", "git", "basic authentication username")
|
||||||
bootstrapGitCmd.Flags().StringVarP(&gitArgs.password, "password", "p", "", "basic authentication password")
|
bootstrapGitCmd.Flags().StringVarP(&gitArgs.password, "password", "p", "", "basic authentication password")
|
||||||
|
bootstrapGitCmd.Flags().BoolVarP(&gitArgs.silent, "silent", "s", false, "assumes the deploy key is already setup, skips confirmation")
|
||||||
|
|
||||||
bootstrapCmd.AddCommand(bootstrapGitCmd)
|
bootstrapCmd.AddCommand(bootstrapGitCmd)
|
||||||
}
|
}
|
||||||
@@ -116,7 +117,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
defer os.RemoveAll(manifestsBase)
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
// Lazy go-git repository
|
// Lazy go-git repository
|
||||||
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
}
|
}
|
||||||
@@ -247,13 +248,16 @@ func promptPublicKey(ctx context.Context, secret corev1.Secret, _ sourcesecret.O
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Successf("public key: %s", strings.TrimSpace(ppk))
|
logger.Successf("public key: %s", strings.TrimSpace(ppk))
|
||||||
prompt := promptui.Prompt{
|
|
||||||
Label: "Please give the key access to your repository",
|
if !gitArgs.silent {
|
||||||
IsConfirm: true,
|
prompt := promptui.Prompt{
|
||||||
}
|
Label: "Please give the key access to your repository",
|
||||||
_, err := prompt.Run()
|
IsConfirm: true,
|
||||||
if err != nil {
|
}
|
||||||
return fmt.Errorf("aborting")
|
_, err := prompt.Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("aborting")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -146,7 +145,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lazy go-git repository
|
// Lazy go-git repository
|
||||||
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -159,7 +158,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lazy go-git repository
|
// Lazy go-git repository
|
||||||
tmpDir, err := ioutil.TempDir("", "flux-bootstrap-")
|
tmpDir, err := os.MkdirTemp("", "flux-bootstrap-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
@@ -87,7 +87,8 @@ var createHelmReleaseCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Create a HelmRelease targeting another namespace than the resource
|
# Create a HelmRelease targeting another namespace than the resource
|
||||||
flux create hr podinfo \
|
flux create hr podinfo \
|
||||||
--target-namespace=default \
|
--target-namespace=test \
|
||||||
|
--create-target-namespace=true \
|
||||||
--source=HelmRepository/podinfo \
|
--source=HelmRepository/podinfo \
|
||||||
--chart=podinfo
|
--chart=podinfo
|
||||||
|
|
||||||
@@ -113,6 +114,7 @@ type helmReleaseFlags struct {
|
|||||||
chart string
|
chart string
|
||||||
chartVersion string
|
chartVersion string
|
||||||
targetNamespace string
|
targetNamespace string
|
||||||
|
createNamespace bool
|
||||||
valuesFiles []string
|
valuesFiles []string
|
||||||
valuesFrom flags.HelmReleaseValuesFrom
|
valuesFrom flags.HelmReleaseValuesFrom
|
||||||
saName string
|
saName string
|
||||||
@@ -128,6 +130,7 @@ func init() {
|
|||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.chartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
|
||||||
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.dependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
|
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.dependsOn, "depends-on", nil, "HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'")
|
||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.targetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.targetNamespace, "target-namespace", "", "namespace to install this release, defaults to the HelmRelease namespace")
|
||||||
|
createHelmReleaseCmd.Flags().BoolVar(&helmReleaseArgs.createNamespace, "create-target-namespace", false, "create the target namespace if it does not exist")
|
||||||
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
|
createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
|
||||||
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.valuesFiles, "values", nil, "local path to values.yaml files, also accepts comma-separated values")
|
createHelmReleaseCmd.Flags().StringSliceVar(&helmReleaseArgs.valuesFiles, "values", nil, "local path to values.yaml files, also accepts comma-separated values")
|
||||||
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.valuesFrom, "values-from", helmReleaseArgs.valuesFrom.Description())
|
createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.valuesFrom, "values-from", helmReleaseArgs.valuesFrom.Description())
|
||||||
@@ -167,6 +170,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Duration: createArgs.interval,
|
Duration: createArgs.interval,
|
||||||
},
|
},
|
||||||
TargetNamespace: helmReleaseArgs.targetNamespace,
|
TargetNamespace: helmReleaseArgs.targetNamespace,
|
||||||
|
|
||||||
Chart: helmv2.HelmChartTemplate{
|
Chart: helmv2.HelmChartTemplate{
|
||||||
Spec: helmv2.HelmChartTemplateSpec{
|
Spec: helmv2.HelmChartTemplateSpec{
|
||||||
Chart: helmReleaseArgs.chart,
|
Chart: helmReleaseArgs.chart,
|
||||||
@@ -178,6 +182,9 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Install: &helmv2.Install{
|
||||||
|
CreateNamespace: helmReleaseArgs.createNamespace,
|
||||||
|
},
|
||||||
Suspend: false,
|
Suspend: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -187,14 +194,14 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if helmReleaseArgs.crds != "" {
|
if helmReleaseArgs.crds != "" {
|
||||||
helmRelease.Spec.Install = &helmv2.Install{CRDs: helmv2.Create}
|
helmRelease.Spec.Install.CRDs = helmv2.Create
|
||||||
helmRelease.Spec.Upgrade = &helmv2.Upgrade{CRDs: helmv2.CRDsPolicy(helmReleaseArgs.crds.String())}
|
helmRelease.Spec.Upgrade = &helmv2.Upgrade{CRDs: helmv2.CRDsPolicy(helmReleaseArgs.crds.String())}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(helmReleaseArgs.valuesFiles) > 0 {
|
if len(helmReleaseArgs.valuesFiles) > 0 {
|
||||||
valuesMap := make(map[string]interface{})
|
valuesMap := make(map[string]interface{})
|
||||||
for _, v := range helmReleaseArgs.valuesFiles {
|
for _, v := range helmReleaseArgs.valuesFiles {
|
||||||
data, err := ioutil.ReadFile(v)
|
data, err := os.ReadFile(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("reading values from %s failed: %w", v, err)
|
return fmt.Errorf("reading values from %s failed: %w", v, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createImagePolicyCmd = &cobra.Command{
|
var createImagePolicyCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createImageRepositoryCmd = &cobra.Command{
|
var createImageRepositoryCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -63,19 +63,15 @@ For Git over HTTP/S, the provided basic authentication credentials are stored in
|
|||||||
--username=username \
|
--username=username \
|
||||||
--password=password
|
--password=password
|
||||||
|
|
||||||
# Create a Git SSH secret on disk and print the deploy key
|
# Create a Git SSH secret on disk
|
||||||
flux create secret git podinfo-auth \
|
flux create secret git podinfo-auth \
|
||||||
--url=ssh://git@github.com/stefanprodan/podinfo \
|
--url=ssh://git@github.com/stefanprodan/podinfo \
|
||||||
--export > podinfo-auth.yaml
|
--export > podinfo-auth.yaml
|
||||||
|
|
||||||
yq read podinfo-auth.yaml 'data."identity.pub"' | base64 --decode
|
# Print the deploy key
|
||||||
|
yq eval '.stringData."identity.pub"' podinfo-auth.yaml
|
||||||
# Create a Git SSH secret on disk and encrypt it with Mozilla SOPS
|
|
||||||
flux create secret git podinfo-auth \
|
|
||||||
--namespace=apps \
|
|
||||||
--url=ssh://git@github.com/stefanprodan/podinfo \
|
|
||||||
--export > podinfo-auth.yaml
|
|
||||||
|
|
||||||
|
# Encrypt the secret on disk with Mozilla SOPS
|
||||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||||
--in-place podinfo-auth.yaml`,
|
--in-place podinfo-auth.yaml`,
|
||||||
RunE: createSecretGitCmdRun,
|
RunE: createSecretGitCmdRun,
|
||||||
@@ -187,7 +183,7 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if err := upsertSecret(ctx, kubeClient, s); err != nil {
|
if err := upsertSecret(ctx, kubeClient, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Actionf("secret '%s' created in '%s' namespace", name, rootArgs.namespace)
|
logger.Actionf("git secret '%s' created in '%s' namespace", name, rootArgs.namespace)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,5 +112,6 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Actionf("helm secret '%s' created in '%s' namespace", name, rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,5 +109,6 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Actionf("tls secret '%s' created in '%s' namespace", name, rootArgs.namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -112,7 +111,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", name)
|
tmpDir, err := os.MkdirTemp("", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -178,7 +177,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("recurse submodules requires --git-implementation=%s", sourcev1.GoGitImplementation)
|
return fmt.Errorf("recurse submodules requires --git-implementation=%s", sourcev1.GoGitImplementation)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", name)
|
tmpDir, err := os.MkdirTemp("", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -66,13 +65,14 @@ For private Helm repositories, the basic authentication credentials are stored i
|
|||||||
}
|
}
|
||||||
|
|
||||||
type sourceHelmFlags struct {
|
type sourceHelmFlags struct {
|
||||||
url string
|
url string
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
certFile string
|
certFile string
|
||||||
keyFile string
|
keyFile string
|
||||||
caFile string
|
caFile string
|
||||||
secretRef string
|
secretRef string
|
||||||
|
passCredentials bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceHelmArgs sourceHelmFlags
|
var sourceHelmArgs sourceHelmFlags
|
||||||
@@ -85,6 +85,7 @@ func init() {
|
|||||||
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.keyFile, "key-file", "", "TLS authentication key file path")
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.keyFile, "key-file", "", "TLS authentication key file path")
|
||||||
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.caFile, "ca-file", "", "TLS authentication CA file path")
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.caFile, "ca-file", "", "TLS authentication CA file path")
|
||||||
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.secretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
|
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.secretRef, "secret-ref", "", "", "the name of an existing secret containing TLS or basic auth credentials")
|
||||||
|
createSourceHelmCmd.Flags().BoolVarP(&sourceHelmArgs.passCredentials, "pass-credentials", "", false, "pass credentials to all domains")
|
||||||
|
|
||||||
createSourceCmd.AddCommand(createSourceHelmCmd)
|
createSourceCmd.AddCommand(createSourceHelmCmd)
|
||||||
}
|
}
|
||||||
@@ -104,7 +105,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", name)
|
tmpDir, err := os.MkdirTemp("", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -132,6 +133,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: sourceHelmArgs.secretRef,
|
Name: sourceHelmArgs.secretRef,
|
||||||
}
|
}
|
||||||
|
helmRepository.Spec.PassCredentials = sourceHelmArgs.passCredentials
|
||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
@@ -175,6 +177,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
helmRepository.Spec.SecretRef = &meta.LocalObjectReference{
|
||||||
Name: secretName,
|
Name: secretName,
|
||||||
}
|
}
|
||||||
|
helmRepository.Spec.PassCredentials = sourceHelmArgs.passCredentials
|
||||||
logger.Successf("authentication configured")
|
logger.Successf("authentication configured")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
for i, _ := range tenantArgs.namespaces {
|
for i := range tenantArgs.namespaces {
|
||||||
if err := exportTenant(namespaces[i], accounts[i], roleBindings[i]); err != nil {
|
if err := exportTenant(namespaces[i], accounts[i], roleBindings[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -164,7 +164,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, _ := range tenantArgs.namespaces {
|
for i := range tenantArgs.namespaces {
|
||||||
logger.Actionf("applying namespace %s", namespaces[i].Name)
|
logger.Actionf("applying namespace %s", namespaces[i].Name)
|
||||||
if err := upsertNamespace(ctx, kubeClient, namespaces[i]); err != nil {
|
if err := upsertNamespace(ctx, kubeClient, namespaces[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImagePolicyCmd = &cobra.Command{
|
var deleteImagePolicyCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImageRepositoryCmd = &cobra.Command{
|
var deleteImageRepositoryCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImageUpdateCmd = &cobra.Command{
|
var deleteImageUpdateCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportImagePolicyCmd = &cobra.Command{
|
var exportImagePolicyCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportImageRepositoryCmd = &cobra.Command{
|
var exportImageRepositoryCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportImageUpdateCmd = &cobra.Command{
|
var exportImageUpdateCmd = &cobra.Command{
|
||||||
|
|||||||
149
cmd/flux/get.go
149
cmd/flux/get.go
@@ -25,6 +25,9 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
watchtools "k8s.io/client-go/tools/watch"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
@@ -32,6 +35,26 @@ import (
|
|||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type deriveType func(runtime.Object) (summarisable, error)
|
||||||
|
|
||||||
|
type typeMap map[string]deriveType
|
||||||
|
|
||||||
|
func (m typeMap) registerCommand(t string, f deriveType) error {
|
||||||
|
if _, ok := m[t]; ok {
|
||||||
|
return fmt.Errorf("duplicate type function %s", t)
|
||||||
|
}
|
||||||
|
m[t] = f
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m typeMap) execute(t string, obj runtime.Object) (summarisable, error) {
|
||||||
|
f, ok := m[t]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unsupported type %s", t)
|
||||||
|
}
|
||||||
|
return f(obj)
|
||||||
|
}
|
||||||
|
|
||||||
var getCmd = &cobra.Command{
|
var getCmd = &cobra.Command{
|
||||||
Use: "get",
|
Use: "get",
|
||||||
Short: "Get the resources and their status",
|
Short: "Get the resources and their status",
|
||||||
@@ -39,7 +62,10 @@ var getCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetFlags struct {
|
type GetFlags struct {
|
||||||
allNamespaces bool
|
allNamespaces bool
|
||||||
|
noHeader bool
|
||||||
|
statusSelector string
|
||||||
|
watch bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var getArgs GetFlags
|
var getArgs GetFlags
|
||||||
@@ -47,6 +73,10 @@ var getArgs GetFlags
|
|||||||
func init() {
|
func init() {
|
||||||
getCmd.PersistentFlags().BoolVarP(&getArgs.allNamespaces, "all-namespaces", "A", false,
|
getCmd.PersistentFlags().BoolVarP(&getArgs.allNamespaces, "all-namespaces", "A", false,
|
||||||
"list the requested object(s) across all namespaces")
|
"list the requested object(s) across all namespaces")
|
||||||
|
getCmd.PersistentFlags().BoolVarP(&getArgs.noHeader, "no-header", "", false, "skip the header when printing the results")
|
||||||
|
getCmd.PersistentFlags().BoolVarP(&getArgs.watch, "watch", "w", false, "After listing/getting the requested object, watch for changes.")
|
||||||
|
getCmd.PersistentFlags().StringVar(&getArgs.statusSelector, "status-selector", "",
|
||||||
|
"specify the status condition name and the desired state to filter the get result, e.g. ready=false")
|
||||||
rootCmd.AddCommand(getCmd)
|
rootCmd.AddCommand(getCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +84,7 @@ type summarisable interface {
|
|||||||
listAdapter
|
listAdapter
|
||||||
summariseItem(i int, includeNamespace bool, includeKind bool) []string
|
summariseItem(i int, includeNamespace bool, includeKind bool) []string
|
||||||
headers(includeNamespace bool) []string
|
headers(includeNamespace bool) []string
|
||||||
|
statusSelectorMatches(i int, conditionType, conditionStatus string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- these help with implementations of summarisable
|
// --- these help with implementations of summarisable
|
||||||
@@ -65,6 +96,20 @@ func statusAndMessage(conditions []metav1.Condition) (string, string) {
|
|||||||
return string(metav1.ConditionFalse), "waiting to be reconciled"
|
return string(metav1.ConditionFalse), "waiting to be reconciled"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func statusMatches(conditionType, conditionStatus string, conditions []metav1.Condition) bool {
|
||||||
|
// we don't use apimeta.FindStatusCondition because we'd like to use EqualFold to compare two strings
|
||||||
|
var c *metav1.Condition
|
||||||
|
for i := range conditions {
|
||||||
|
if strings.EqualFold(conditions[i].Type, conditionType) {
|
||||||
|
c = &conditions[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c != nil {
|
||||||
|
return strings.EqualFold(string(c.Status), conditionStatus)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func nameColumns(item named, includeNamespace bool, includeKind bool) []string {
|
func nameColumns(item named, includeNamespace bool, includeKind bool) []string {
|
||||||
name := item.GetName()
|
name := item.GetName()
|
||||||
if includeKind {
|
if includeKind {
|
||||||
@@ -82,7 +127,8 @@ var namespaceHeader = []string{"Namespace"}
|
|||||||
|
|
||||||
type getCommand struct {
|
type getCommand struct {
|
||||||
apiType
|
apiType
|
||||||
list summarisable
|
list summarisable
|
||||||
|
funcMap typeMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (get getCommand) run(cmd *cobra.Command, args []string) error {
|
func (get getCommand) run(cmd *cobra.Command, args []string) error {
|
||||||
@@ -103,13 +149,17 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
listOpts = append(listOpts, client.MatchingFields{"metadata.name": args[0]})
|
listOpts = append(listOpts, client.MatchingFields{"metadata.name": args[0]})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAll := cmd.Use == "all"
|
||||||
|
|
||||||
|
if getArgs.watch {
|
||||||
|
return get.watch(ctx, kubeClient, cmd, args, listOpts)
|
||||||
|
}
|
||||||
|
|
||||||
err = kubeClient.List(ctx, get.list.asClientList(), listOpts...)
|
err = kubeClient.List(ctx, get.list.asClientList(), listOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
getAll := cmd.Use == "all"
|
|
||||||
|
|
||||||
if get.list.len() == 0 {
|
if get.list.len() == 0 {
|
||||||
if !getAll {
|
if !getAll {
|
||||||
logger.Failuref("no %s objects found in %s namespace", get.kind, rootArgs.namespace)
|
logger.Failuref("no %s objects found in %s namespace", get.kind, rootArgs.namespace)
|
||||||
@@ -117,16 +167,97 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
header := get.list.headers(getArgs.allNamespaces)
|
var header []string
|
||||||
var rows [][]string
|
if !getArgs.noHeader {
|
||||||
for i := 0; i < get.list.len(); i++ {
|
header = get.list.headers(getArgs.allNamespaces)
|
||||||
row := get.list.summariseItem(i, getArgs.allNamespaces, getAll)
|
|
||||||
rows = append(rows, row)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rows, err := getRowsToPrint(getAll, get.list)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
utils.PrintTable(os.Stdout, header, rows)
|
utils.PrintTable(os.Stdout, header, rows)
|
||||||
|
|
||||||
if getAll {
|
if getAll {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRowsToPrint(getAll bool, list summarisable) ([][]string, error) {
|
||||||
|
noFilter := true
|
||||||
|
var conditionType, conditionStatus string
|
||||||
|
if getArgs.statusSelector != "" {
|
||||||
|
parts := strings.SplitN(getArgs.statusSelector, "=", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, fmt.Errorf("expected status selector in type=status format, but found: %s", getArgs.statusSelector)
|
||||||
|
}
|
||||||
|
conditionType = parts[0]
|
||||||
|
conditionStatus = parts[1]
|
||||||
|
noFilter = false
|
||||||
|
}
|
||||||
|
var rows [][]string
|
||||||
|
for i := 0; i < list.len(); i++ {
|
||||||
|
if noFilter || list.statusSelectorMatches(i, conditionType, conditionStatus) {
|
||||||
|
row := list.summariseItem(i, getArgs.allNamespaces, getAll)
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// watch starts a client-side watch of one or more resources.
|
||||||
|
func (get *getCommand) watch(ctx context.Context, kubeClient client.WithWatch, cmd *cobra.Command, args []string, listOpts []client.ListOption) error {
|
||||||
|
w, err := kubeClient.Watch(ctx, get.list.asClientList(), listOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = watchUntil(ctx, w, get)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func watchUntil(ctx context.Context, w watch.Interface, get *getCommand) (bool, error) {
|
||||||
|
firstIteration := true
|
||||||
|
_, error := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
|
||||||
|
objToPrint := e.Object
|
||||||
|
sink, err := get.funcMap.execute(get.apiType.kind, objToPrint)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var header []string
|
||||||
|
if !getArgs.noHeader {
|
||||||
|
header = sink.headers(getArgs.allNamespaces)
|
||||||
|
}
|
||||||
|
rows, err := getRowsToPrint(false, sink)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if firstIteration {
|
||||||
|
utils.PrintTable(os.Stdout, header, rows)
|
||||||
|
firstIteration = false
|
||||||
|
} else {
|
||||||
|
utils.PrintTable(os.Stdout, []string{}, rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return false, error
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateWatchOption(cmd *cobra.Command, toMatch string) error {
|
||||||
|
w, _ := cmd.Flags().GetBool("watch")
|
||||||
|
if cmd.Use == toMatch && w {
|
||||||
|
return fmt.Errorf("expected a single resource type, but found %s", cmd.Use)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -32,10 +34,39 @@ var getAlertCmd = &cobra.Command{
|
|||||||
Long: "The get alert command prints the statuses of the resources.",
|
Long: "The get alert command prints the statuses of the resources.",
|
||||||
Example: ` # List all Alerts and their status
|
Example: ` # List all Alerts and their status
|
||||||
flux get alerts`,
|
flux get alerts`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: alertType,
|
get := getCommand{
|
||||||
list: &alertListAdapter{¬ificationv1.AlertList{}},
|
apiType: alertType,
|
||||||
}.run,
|
list: &alertListAdapter{¬ificationv1.AlertList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*notificationv1.Alert)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v alert", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := alertListAdapter{
|
||||||
|
¬ificationv1.AlertList{
|
||||||
|
Items: []notificationv1.Alert{
|
||||||
|
*o,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -55,3 +86,8 @@ func (s alertListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s alertListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -29,10 +32,39 @@ var getAlertProviderCmd = &cobra.Command{
|
|||||||
Long: "The get alert-provider command prints the statuses of the resources.",
|
Long: "The get alert-provider command prints the statuses of the resources.",
|
||||||
Example: ` # List all Providers and their status
|
Example: ` # List all Providers and their status
|
||||||
flux get alert-providers`,
|
flux get alert-providers`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: alertProviderType,
|
get := getCommand{
|
||||||
list: alertProviderListAdapter{¬ificationv1.ProviderList{}},
|
apiType: alertProviderType,
|
||||||
}.run,
|
list: alertProviderListAdapter{¬ificationv1.ProviderList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*notificationv1.Provider)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v alert-provider", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := alertProviderListAdapter{
|
||||||
|
¬ificationv1.ProviderList{
|
||||||
|
Items: []notificationv1.Provider{
|
||||||
|
*o,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -52,3 +84,8 @@ func (s alertProviderListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s alertProviderListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,7 +36,12 @@ var getAllCmd = &cobra.Command{
|
|||||||
# List all resources in all namespaces
|
# List all resources in all namespaces
|
||||||
flux get all --all-namespaces`,
|
flux get all --all-namespaces`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
err := getSourceAllCmd.RunE(cmd, args)
|
err := validateWatchOption(cmd, "all")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = getSourceAllCmd.RunE(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logError(err)
|
logError(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getHelmReleaseCmd = &cobra.Command{
|
var getHelmReleaseCmd = &cobra.Command{
|
||||||
@@ -31,10 +33,36 @@ var getHelmReleaseCmd = &cobra.Command{
|
|||||||
Long: "The get helmreleases command prints the statuses of the resources.",
|
Long: "The get helmreleases command prints the statuses of the resources.",
|
||||||
Example: ` # List all Helm releases and their status
|
Example: ` # List all Helm releases and their status
|
||||||
flux get helmreleases`,
|
flux get helmreleases`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: helmReleaseType,
|
get := getCommand{
|
||||||
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
apiType: helmReleaseType,
|
||||||
}.run,
|
list: &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*helmv2.HelmRelease)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v helmrelease", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := helmReleaseListAdapter{&helmv2.HelmReleaseList{
|
||||||
|
Items: []helmv2.HelmRelease{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -56,3 +84,8 @@ func (a helmReleaseListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a helmReleaseListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := a.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ var getImageCmd = &cobra.Command{
|
|||||||
Aliases: []string{"image"},
|
Aliases: []string{"image"},
|
||||||
Short: "Get image automation object status",
|
Short: "Get image automation object status",
|
||||||
Long: "The get image sub-commands print the status of image automation objects.",
|
Long: "The get image sub-commands print the status of image automation objects.",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return validateWatchOption(cmd, "images")
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImageAllCmd = &cobra.Command{
|
var getImageAllCmd = &cobra.Command{
|
||||||
@@ -35,6 +35,11 @@ var getImageAllCmd = &cobra.Command{
|
|||||||
# List all image objects in all namespaces
|
# List all image objects in all namespaces
|
||||||
flux get images all --all-namespaces`,
|
flux get images all --all-namespaces`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := validateWatchOption(cmd, "all")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var allImageCmd = []getCommand{
|
var allImageCmd = []getCommand{
|
||||||
{
|
{
|
||||||
apiType: imageRepositoryType,
|
apiType: imageRepositoryType,
|
||||||
|
|||||||
@@ -17,9 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"fmt"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImagePolicyCmd = &cobra.Command{
|
var getImagePolicyCmd = &cobra.Command{
|
||||||
@@ -31,10 +34,36 @@ var getImagePolicyCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List image policies from all namespaces
|
# List image policies from all namespaces
|
||||||
flux get image policy --all-namespaces`,
|
flux get image policy --all-namespaces`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: imagePolicyType,
|
get := getCommand{
|
||||||
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
apiType: imagePolicyType,
|
||||||
}.run,
|
list: &imagePolicyListAdapter{&imagev1.ImagePolicyList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*imagev1.ImagePolicy)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v policy", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := imagePolicyListAdapter{&imagev1.ImagePolicyList{
|
||||||
|
Items: []imagev1.ImagePolicy{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -54,3 +83,8 @@ func (s imagePolicyListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s imagePolicyListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,13 +17,15 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImageRepositoryCmd = &cobra.Command{
|
var getImageRepositoryCmd = &cobra.Command{
|
||||||
@@ -35,10 +37,36 @@ var getImageRepositoryCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List image repositories from all namespaces
|
# List image repositories from all namespaces
|
||||||
flux get image repository --all-namespaces`,
|
flux get image repository --all-namespaces`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: imageRepositoryType,
|
get := getCommand{
|
||||||
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
apiType: imageRepositoryType,
|
||||||
}.run,
|
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*imagev1.ImageRepository)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v repository", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := imageRepositoryListAdapter{&imagev1.ImageRepositoryList{
|
||||||
|
Items: []imagev1.ImageRepository{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -63,3 +91,8 @@ func (s imageRepositoryListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s imageRepositoryListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,13 +17,15 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getImageUpdateCmd = &cobra.Command{
|
var getImageUpdateCmd = &cobra.Command{
|
||||||
@@ -35,10 +37,36 @@ var getImageUpdateCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List image update automations from all namespaces
|
# List image update automations from all namespaces
|
||||||
flux get image update --all-namespaces`,
|
flux get image update --all-namespaces`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: imageUpdateAutomationType,
|
get := getCommand{
|
||||||
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
apiType: imageUpdateAutomationType,
|
||||||
}.run,
|
list: &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*autov1.ImageUpdateAutomation)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v update", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{
|
||||||
|
Items: []autov1.ImageUpdateAutomation{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -62,3 +90,8 @@ func (s imageUpdateAutomationListAdapter) headers(includeNamespace bool) []strin
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s imageUpdateAutomationListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -32,10 +34,39 @@ var getKsCmd = &cobra.Command{
|
|||||||
Long: "The get kustomizations command prints the statuses of the resources.",
|
Long: "The get kustomizations command prints the statuses of the resources.",
|
||||||
Example: ` # List all kustomizations and their status
|
Example: ` # List all kustomizations and their status
|
||||||
flux get kustomizations`,
|
flux get kustomizations`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: kustomizationType,
|
get := getCommand{
|
||||||
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
apiType: kustomizationType,
|
||||||
}.run,
|
list: &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*kustomizev1.Kustomization)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v kustomization", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := kustomizationListAdapter{
|
||||||
|
&kustomizev1.KustomizationList{
|
||||||
|
Items: []kustomizev1.Kustomization{
|
||||||
|
*o,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -57,3 +88,8 @@ func (a kustomizationListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a kustomizationListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := a.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -32,10 +34,36 @@ var getReceiverCmd = &cobra.Command{
|
|||||||
Long: "The get receiver command prints the statuses of the resources.",
|
Long: "The get receiver command prints the statuses of the resources.",
|
||||||
Example: ` # List all Receiver and their status
|
Example: ` # List all Receiver and their status
|
||||||
flux get receivers`,
|
flux get receivers`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: receiverType,
|
get := getCommand{
|
||||||
list: receiverListAdapter{¬ificationv1.ReceiverList{}},
|
apiType: receiverType,
|
||||||
}.run,
|
list: receiverListAdapter{¬ificationv1.ReceiverList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*notificationv1.Receiver)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v receiver", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := receiverListAdapter{¬ificationv1.ReceiverList{
|
||||||
|
Items: []notificationv1.Receiver{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -55,3 +83,8 @@ func (s receiverListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s receiverListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := s.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ var getSourceCmd = &cobra.Command{
|
|||||||
Aliases: []string{"source"},
|
Aliases: []string{"source"},
|
||||||
Short: "Get source statuses",
|
Short: "Get source statuses",
|
||||||
Long: "The get source sub-commands print the statuses of the sources.",
|
Long: "The get source sub-commands print the statuses of the sources.",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
return validateWatchOption(cmd, "sources")
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ var getSourceAllCmd = &cobra.Command{
|
|||||||
# List all sources in all namespaces
|
# List all sources in all namespaces
|
||||||
flux get sources all --all-namespaces`,
|
flux get sources all --all-namespaces`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := validateWatchOption(cmd, "all")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var allSourceCmd = []getCommand{
|
var allSourceCmd = []getCommand{
|
||||||
{
|
{
|
||||||
apiType: bucketType,
|
apiType: bucketType,
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -34,10 +36,36 @@ var getSourceBucketCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List buckets from all namespaces
|
# List buckets from all namespaces
|
||||||
flux get sources helm --all-namespaces`,
|
flux get sources helm --all-namespaces`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: bucketType,
|
get := getCommand{
|
||||||
list: &bucketListAdapter{&sourcev1.BucketList{}},
|
apiType: bucketType,
|
||||||
}.run,
|
list: &bucketListAdapter{&sourcev1.BucketList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*sourcev1.Bucket)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v bucket", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := &bucketListAdapter{&sourcev1.BucketList{
|
||||||
|
Items: []sourcev1.Bucket{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -62,3 +90,8 @@ func (a bucketListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a bucketListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := a.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -34,10 +36,36 @@ var getSourceHelmChartCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List Helm charts from all namespaces
|
# List Helm charts from all namespaces
|
||||||
flux get sources chart --all-namespaces`,
|
flux get sources chart --all-namespaces`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: helmChartType,
|
get := getCommand{
|
||||||
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
|
apiType: helmChartType,
|
||||||
}.run,
|
list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*sourcev1.HelmChart)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v chart", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := &helmChartListAdapter{&sourcev1.HelmChartList{
|
||||||
|
Items: []sourcev1.HelmChart{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -62,3 +90,8 @@ func (a helmChartListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a helmChartListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := a.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -34,10 +36,36 @@ var getSourceGitCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List Git repositories from all namespaces
|
# List Git repositories from all namespaces
|
||||||
flux get sources git --all-namespaces`,
|
flux get sources git --all-namespaces`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: gitRepositoryType,
|
get := getCommand{
|
||||||
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
apiType: gitRepositoryType,
|
||||||
}.run,
|
list: &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*sourcev1.GitRepository)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v git", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := &gitRepositoryListAdapter{&sourcev1.GitRepositoryList{
|
||||||
|
Items: []sourcev1.GitRepository{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -62,3 +90,8 @@ func (a gitRepositoryListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a gitRepositoryListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := a.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
@@ -34,10 +36,36 @@ var getSourceHelmCmd = &cobra.Command{
|
|||||||
|
|
||||||
# List Helm repositories from all namespaces
|
# List Helm repositories from all namespaces
|
||||||
flux get sources helm --all-namespaces`,
|
flux get sources helm --all-namespaces`,
|
||||||
RunE: getCommand{
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
apiType: helmRepositoryType,
|
get := getCommand{
|
||||||
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
apiType: helmRepositoryType,
|
||||||
}.run,
|
list: &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
|
||||||
|
funcMap: make(typeMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := get.funcMap.registerCommand(get.apiType.kind, func(obj runtime.Object) (summarisable, error) {
|
||||||
|
o, ok := obj.(*sourcev1.HelmRepository)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Impossible to cast type %#v helm", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
sink := &helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{
|
||||||
|
Items: []sourcev1.HelmRepository{
|
||||||
|
*o,
|
||||||
|
}}}
|
||||||
|
return sink, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := get.run(cmd, args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -62,3 +90,8 @@ func (a helmRepositoryListAdapter) headers(includeNamespace bool) []string {
|
|||||||
}
|
}
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a helmRepositoryListAdapter) statusSelectorMatches(i int, conditionType, conditionStatus string) bool {
|
||||||
|
item := a.Items[i]
|
||||||
|
return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ func (h helmReleaseAdapter) asClientObject() client.Object {
|
|||||||
return h.HelmRelease
|
return h.HelmRelease
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h helmReleaseAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return h.HelmRelease.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// helmv2.HelmReleaseList
|
// helmv2.HelmReleaseList
|
||||||
|
|
||||||
type helmReleaseListAdapter struct {
|
type helmReleaseListAdapter struct {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These are general-purpose adapters for attaching methods to, for
|
// These are general-purpose adapters for attaching methods to, for
|
||||||
@@ -42,6 +42,10 @@ func (a imageRepositoryAdapter) asClientObject() client.Object {
|
|||||||
return a.ImageRepository
|
return a.ImageRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a imageRepositoryAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.ImageRepository.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// imagev1.ImageRepositoryList
|
// imagev1.ImageRepositoryList
|
||||||
|
|
||||||
type imageRepositoryListAdapter struct {
|
type imageRepositoryListAdapter struct {
|
||||||
@@ -100,6 +104,10 @@ func (a imageUpdateAutomationAdapter) asClientObject() client.Object {
|
|||||||
return a.ImageUpdateAutomation
|
return a.ImageUpdateAutomation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a imageUpdateAutomationAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.ImageUpdateAutomation.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// autov1.ImageUpdateAutomationList
|
// autov1.ImageUpdateAutomationList
|
||||||
|
|
||||||
type imageUpdateAutomationListAdapter struct {
|
type imageUpdateAutomationListAdapter struct {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -132,7 +131,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
logger.Generatef("generating manifests")
|
logger.Generatef("generating manifests")
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir, err := ioutil.TempDir("", rootArgs.namespace)
|
tmpDir, err := os.MkdirTemp("", rootArgs.namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ func (a kustomizationAdapter) asClientObject() client.Object {
|
|||||||
return a.Kustomization
|
return a.Kustomization
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a kustomizationAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.Kustomization.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// kustomizev1.KustomizationList
|
// kustomizev1.KustomizationList
|
||||||
|
|
||||||
type kustomizationListAdapter struct {
|
type kustomizationListAdapter struct {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -126,6 +127,18 @@ func NewRootFlags() rootFlags {
|
|||||||
return rf
|
return rf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type rootContext struct {
|
||||||
|
kubeManager utils.KubeManager
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootCtx = NewRootContext()
|
||||||
|
|
||||||
|
func NewRootContext() rootContext {
|
||||||
|
var rc rootContext
|
||||||
|
rc.kubeManager = utils.DefaultKubeManager()
|
||||||
|
return rc
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
configureKubeconfig()
|
configureKubeconfig()
|
||||||
|
|||||||
158
cmd/flux/main_test.go
Normal file
158
cmd/flux/main_test.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/mattn/go-shellwords"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
// Ensure tests print consistent timestamps regardless of timezone
|
||||||
|
os.Setenv("TZ", "UTC")
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
func readYamlObjects(objectFile string) ([]client.Object, error) {
|
||||||
|
obj, err := os.ReadFile(objectFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
objects := []client.Object{}
|
||||||
|
reader := k8syaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(obj)))
|
||||||
|
for {
|
||||||
|
doc, err := reader.Read()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unstructuredObj := &unstructured.Unstructured{}
|
||||||
|
decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(doc), len(doc))
|
||||||
|
err = decoder.Decode(unstructuredObj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
objects = append(objects, unstructuredObj)
|
||||||
|
}
|
||||||
|
return objects, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A KubeManager that can create objects that are subject to a test.
|
||||||
|
type fakeKubeManager struct {
|
||||||
|
fakeClient client.WithWatch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *fakeKubeManager) NewClient(kubeconfig string, kubecontext string) (client.WithWatch, error) {
|
||||||
|
return m.fakeClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *fakeKubeManager) CreateObjects(clientObjects []client.Object) error {
|
||||||
|
for _, obj := range clientObjects {
|
||||||
|
err := m.fakeClient.Create(context.Background(), obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFakeKubeManager() *fakeKubeManager {
|
||||||
|
c := fakeclient.NewClientBuilder().WithScheme(utils.NewScheme()).Build()
|
||||||
|
return &fakeKubeManager{
|
||||||
|
fakeClient: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the command and return the captured output.
|
||||||
|
func executeCommand(cmd string) (string, error) {
|
||||||
|
args, err := shellwords.Parse(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
rootCmd.SetOut(buf)
|
||||||
|
rootCmd.SetErr(buf)
|
||||||
|
rootCmd.SetArgs(args)
|
||||||
|
|
||||||
|
_, err = rootCmd.ExecuteC()
|
||||||
|
result := buf.String()
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structure used for each test to load objects into kubernetes, run
|
||||||
|
// commands and assert on the expected output.
|
||||||
|
type cmdTestCase struct {
|
||||||
|
// The command line arguments to test.
|
||||||
|
args string
|
||||||
|
// When true, the test expects the command to fail.
|
||||||
|
wantError bool
|
||||||
|
// String literal that contains the expected test output.
|
||||||
|
goldenValue string
|
||||||
|
// Filename that contains the expected test output.
|
||||||
|
goldenFile string
|
||||||
|
// Filename that contains yaml objects to load into Kubernetes
|
||||||
|
objectFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *cmdTestCase) runTestCmd(t *testing.T) {
|
||||||
|
km := NewFakeKubeManager()
|
||||||
|
rootCtx.kubeManager = km
|
||||||
|
|
||||||
|
if cmd.objectFile != "" {
|
||||||
|
clientObjects, err := readYamlObjects(cmd.objectFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error loading yaml: '%v'", err)
|
||||||
|
}
|
||||||
|
err = km.CreateObjects(clientObjects)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating test objects: '%v'", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := executeCommand(cmd.args)
|
||||||
|
if (err != nil) != cmd.wantError {
|
||||||
|
t.Fatalf("Expected error='%v', Got: %v", cmd.wantError, err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
actual = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected string
|
||||||
|
if cmd.goldenValue != "" {
|
||||||
|
expected = cmd.goldenValue
|
||||||
|
}
|
||||||
|
if cmd.goldenFile != "" {
|
||||||
|
expectedOutput, err := os.ReadFile(cmd.goldenFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error reading golden file: '%s'", err)
|
||||||
|
}
|
||||||
|
expected = string(expectedOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
diff := cmp.Diff(expected, actual)
|
||||||
|
if diff != "" {
|
||||||
|
t.Errorf("Mismatch from '%s' (-want +got):\n%s", cmd.goldenFile, diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersion(t *testing.T) {
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: "--version",
|
||||||
|
goldenValue: "flux version 0.0.0-dev.0\n",
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
}
|
||||||
@@ -39,6 +39,13 @@ type adapter interface {
|
|||||||
asClientObject() client.Object
|
asClientObject() client.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copyable is an interface for a wrapper or alias from which we can
|
||||||
|
// get a deep copied client.Object, required when you e.g. want to
|
||||||
|
// calculate a patch.
|
||||||
|
type copyable interface {
|
||||||
|
deepCopyClientObject() client.Object
|
||||||
|
}
|
||||||
|
|
||||||
// listAdapater is the analogue to adapter, but for lists; the
|
// listAdapater is the analogue to adapter, but for lists; the
|
||||||
// controller runtime distinguishes between methods dealing with
|
// controller runtime distinguishes between methods dealing with
|
||||||
// objects and lists.
|
// objects and lists.
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ func (a receiverAdapter) asClientObject() client.Object {
|
|||||||
return a.Receiver
|
return a.Receiver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a receiverAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.Receiver.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// notificationv1.Receiver
|
// notificationv1.Receiver
|
||||||
|
|
||||||
type receiverListAdapter struct {
|
type receiverListAdapter struct {
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ type reconcileCommand struct {
|
|||||||
|
|
||||||
type reconcilable interface {
|
type reconcilable interface {
|
||||||
adapter // to be able to load from the cluster
|
adapter // to be able to load from the cluster
|
||||||
|
copyable // to be able to calculate patches
|
||||||
suspendable // to tell if it's suspended
|
suspendable // to tell if it's suspended
|
||||||
|
|
||||||
// these are implemented by anything embedding metav1.ObjectMeta
|
// these are implemented by anything embedding metav1.ObjectMeta
|
||||||
@@ -142,6 +143,7 @@ func requestReconciliation(ctx context.Context, kubeClient client.Client,
|
|||||||
if err := kubeClient.Get(ctx, namespacedName, obj.asClientObject()); err != nil {
|
if err := kubeClient.Get(ctx, namespacedName, obj.asClientObject()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
patch := client.MergeFrom(obj.deepCopyClientObject())
|
||||||
if ann := obj.GetAnnotations(); ann == nil {
|
if ann := obj.GetAnnotations(); ann == nil {
|
||||||
obj.SetAnnotations(map[string]string{
|
obj.SetAnnotations(map[string]string{
|
||||||
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
|
||||||
@@ -150,7 +152,7 @@ func requestReconciliation(ctx context.Context, kubeClient client.Client,
|
|||||||
ann[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
ann[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
|
||||||
obj.SetAnnotations(ann)
|
obj.SetAnnotations(ann)
|
||||||
}
|
}
|
||||||
return kubeClient.Update(ctx, obj.asClientObject())
|
return kubeClient.Patch(ctx, obj.asClientObject(), patch)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reconcileImageRepositoryCmd = &cobra.Command{
|
var reconcileImageRepositoryCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
meta "github.com/fluxcd/pkg/apis/meta"
|
meta "github.com/fluxcd/pkg/apis/meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resumeImageRepositoryCmd = &cobra.Command{
|
var resumeImageRepositoryCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resumeImageUpdateCmd = &cobra.Command{
|
var resumeImageUpdateCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ func (a bucketAdapter) asClientObject() client.Object {
|
|||||||
return a.Bucket
|
return a.Bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a bucketAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.Bucket.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// sourcev1.BucketList
|
// sourcev1.BucketList
|
||||||
|
|
||||||
type bucketListAdapter struct {
|
type bucketListAdapter struct {
|
||||||
@@ -70,6 +74,10 @@ func (a helmChartAdapter) asClientObject() client.Object {
|
|||||||
return a.HelmChart
|
return a.HelmChart
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a helmChartAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.HelmChart.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// sourcev1.HelmChartList
|
// sourcev1.HelmChartList
|
||||||
|
|
||||||
type helmChartListAdapter struct {
|
type helmChartListAdapter struct {
|
||||||
@@ -99,6 +107,10 @@ func (a gitRepositoryAdapter) asClientObject() client.Object {
|
|||||||
return a.GitRepository
|
return a.GitRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a gitRepositoryAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.GitRepository.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// sourcev1.GitRepositoryList
|
// sourcev1.GitRepositoryList
|
||||||
|
|
||||||
type gitRepositoryListAdapter struct {
|
type gitRepositoryListAdapter struct {
|
||||||
@@ -128,6 +140,10 @@ func (a helmRepositoryAdapter) asClientObject() client.Object {
|
|||||||
return a.HelmRepository
|
return a.HelmRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a helmRepositoryAdapter) deepCopyClientObject() client.Object {
|
||||||
|
return a.HelmRepository.DeepCopy()
|
||||||
|
}
|
||||||
|
|
||||||
// sourcev1.HelmRepositoryList
|
// sourcev1.HelmRepositoryList
|
||||||
|
|
||||||
type helmRepositoryListAdapter struct {
|
type helmRepositoryListAdapter struct {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var suspendImageRepositoryCmd = &cobra.Command{
|
var suspendImageRepositoryCmd = &cobra.Command{
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var suspendImageUpdateCmd = &cobra.Command{
|
var suspendImageUpdateCmd = &cobra.Command{
|
||||||
|
|||||||
25
cmd/flux/testdata/trace/deployment.txt
vendored
Normal file
25
cmd/flux/testdata/trace/deployment.txt
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Object: deployment/podinfo
|
||||||
|
Namespace: podinfo
|
||||||
|
Status: Managed by Flux
|
||||||
|
---
|
||||||
|
HelmRelease: podinfo
|
||||||
|
Namespace: podinfo
|
||||||
|
Revision: 6.0.0
|
||||||
|
Status: Last reconciled at 2021-07-16 15:42:20 +0000 UTC
|
||||||
|
Message: Release reconciliation succeeded
|
||||||
|
---
|
||||||
|
HelmChart: podinfo-podinfo
|
||||||
|
Namespace: flux-system
|
||||||
|
Chart: podinfo
|
||||||
|
Version: 6.0.0
|
||||||
|
Revision: 6.0.0
|
||||||
|
Status: Last reconciled at 2021-07-16 15:32:09 +0000 UTC
|
||||||
|
Message: Fetched revision: 6.0.0
|
||||||
|
---
|
||||||
|
HelmRepository: podinfo
|
||||||
|
Namespace: flux-system
|
||||||
|
URL: https://stefanprodan.github.io/podinfo
|
||||||
|
Revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
|
||||||
|
Status: Last reconciled at 2021-07-11 00:25:46 +0000 UTC
|
||||||
|
Message: Fetched revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
|
||||||
129
cmd/flux/testdata/trace/deployment.yaml
vendored
Normal file
129
cmd/flux/testdata/trace/deployment.yaml
vendored
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/managed-by: Helm
|
||||||
|
app.kubernetes.io/name: podinfo
|
||||||
|
helm.toolkit.fluxcd.io/name: podinfo
|
||||||
|
helm.toolkit.fluxcd.io/namespace: podinfo
|
||||||
|
name: podinfo
|
||||||
|
namespace: podinfo
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: podinfo
|
||||||
|
spec:
|
||||||
|
---
|
||||||
|
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||||
|
kind: HelmRelease
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
kustomize.toolkit.fluxcd.io/name: infrastructure
|
||||||
|
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||||
|
name: podinfo
|
||||||
|
namespace: podinfo
|
||||||
|
spec:
|
||||||
|
chart:
|
||||||
|
spec:
|
||||||
|
chart: podinfo
|
||||||
|
sourceRef:
|
||||||
|
kind: HelmRepository
|
||||||
|
name: podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2021-07-16T15:42:20Z"
|
||||||
|
message: Release reconciliation succeeded
|
||||||
|
reason: ReconciliationSucceeded
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
||||||
|
helmChart: flux-system/podinfo-podinfo
|
||||||
|
lastAppliedRevision: 6.0.0
|
||||||
|
lastAttemptedRevision: 6.0.0
|
||||||
|
lastAttemptedValuesChecksum: c31db75d05b7515eba2eef47bd71038c74b2e531
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||||
|
kind: HelmChart
|
||||||
|
metadata:
|
||||||
|
name: podinfo-podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
chart: podinfo
|
||||||
|
sourceRef:
|
||||||
|
kind: HelmRepository
|
||||||
|
name: podinfo
|
||||||
|
version: 6.0.0
|
||||||
|
status:
|
||||||
|
artifact:
|
||||||
|
checksum: cf13ba96773d9a879cd052c86e73199b3f96c854
|
||||||
|
lastUpdateTime: "2021-08-01T04:42:55Z"
|
||||||
|
revision: 6.0.0
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2021-07-16T15:32:09Z"
|
||||||
|
message: 'Fetched revision: 6.0.0'
|
||||||
|
reason: ChartPullSucceeded
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||||
|
kind: HelmRepository
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
kustomize.toolkit.fluxcd.io/name: infrastructure
|
||||||
|
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||||
|
name: podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 5m
|
||||||
|
timeout: 1m0s
|
||||||
|
url: https://stefanprodan.github.io/podinfo
|
||||||
|
status:
|
||||||
|
artifact:
|
||||||
|
checksum: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
|
||||||
|
lastUpdateTime: "2021-07-11T00:25:46Z"
|
||||||
|
revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2021-07-11T00:25:46Z"
|
||||||
|
message: 'Fetched revision: 8411f23d07d3701f0e96e7d9e503b7936d7e1d56'
|
||||||
|
reason: IndexationSucceed
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
||||||
|
---
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: infrastructure
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
path: ./infrastructure/
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: flux-system
|
||||||
|
validation: client
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2021-08-01T04:52:56Z"
|
||||||
|
message: 'Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||||
|
reason: ReconciliationSucceeded
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
kustomize.toolkit.fluxcd.io/name: flux-system
|
||||||
|
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||||
|
name: flux-system
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
gitImplementation: go-git
|
||||||
|
ref:
|
||||||
|
branch: main
|
||||||
|
secretRef:
|
||||||
|
name: flux-system
|
||||||
|
|
||||||
18
cmd/flux/testdata/trace/helmrelease-missing-git-ref.txt
vendored
Normal file
18
cmd/flux/testdata/trace/helmrelease-missing-git-ref.txt
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
Object: HelmRelease/podinfo
|
||||||
|
Namespace: podinfo
|
||||||
|
Status: Managed by Flux
|
||||||
|
---
|
||||||
|
Kustomization: infrastructure
|
||||||
|
Namespace: flux-system
|
||||||
|
Path: ./infrastructure
|
||||||
|
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
|
Status: Last reconciled at 2021-08-01 04:52:56 +0000 UTC
|
||||||
|
Message: Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
|
---
|
||||||
|
GitRepository: flux-system
|
||||||
|
Namespace: flux-system
|
||||||
|
URL: ssh://git@github.com/example/repo
|
||||||
|
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
|
Status: Last reconciled at 2021-07-20 00:48:16 +0000 UTC
|
||||||
|
Message: Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
73
cmd/flux/testdata/trace/helmrelease-missing-git-ref.yaml
vendored
Normal file
73
cmd/flux/testdata/trace/helmrelease-missing-git-ref.yaml
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||||
|
kind: HelmRelease
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
kustomize.toolkit.fluxcd.io/name: infrastructure
|
||||||
|
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||||
|
name: podinfo
|
||||||
|
namespace: podinfo
|
||||||
|
spec:
|
||||||
|
chart:
|
||||||
|
spec:
|
||||||
|
chart: podinfo
|
||||||
|
sourceRef:
|
||||||
|
kind: HelmRepository
|
||||||
|
name: podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2021-07-16T15:42:20Z"
|
||||||
|
message: Release reconciliation succeeded
|
||||||
|
reason: ReconciliationSucceeded
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
||||||
|
helmChart: flux-system/podinfo-podinfo
|
||||||
|
lastAppliedRevision: 6.0.0
|
||||||
|
lastAttemptedRevision: 6.0.0
|
||||||
|
lastAttemptedValuesChecksum: c31db75d05b7515eba2eef47bd71038c74b2e531
|
||||||
|
---
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: infrastructure
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
path: ./infrastructure
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: flux-system
|
||||||
|
validation: client
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2021-08-01T04:52:56Z"
|
||||||
|
message: 'Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||||
|
reason: ReconciliationSucceeded
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
||||||
|
lastAppliedRevision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
kustomize.toolkit.fluxcd.io/name: flux-system
|
||||||
|
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||||
|
name: flux-system
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
gitImplementation: go-git
|
||||||
|
secretRef:
|
||||||
|
name: flux-system
|
||||||
|
url: ssh://git@github.com/example/repo
|
||||||
|
status:
|
||||||
|
artifact:
|
||||||
|
lastUpdateTime: "2021-08-01T04:28:42Z"
|
||||||
|
revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2021-07-20T00:48:16Z"
|
||||||
|
message: 'Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||||
|
reason: GitOperationSucceed
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
||||||
|
|
||||||
19
cmd/flux/testdata/trace/helmrelease.txt
vendored
Normal file
19
cmd/flux/testdata/trace/helmrelease.txt
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
Object: HelmRelease/podinfo
|
||||||
|
Namespace: podinfo
|
||||||
|
Status: Managed by Flux
|
||||||
|
---
|
||||||
|
Kustomization: infrastructure
|
||||||
|
Namespace: flux-system
|
||||||
|
Path: ./infrastructure
|
||||||
|
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
|
Status: Last reconciled at 2021-08-01 04:52:56 +0000 UTC
|
||||||
|
Message: Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
|
---
|
||||||
|
GitRepository: flux-system
|
||||||
|
Namespace: flux-system
|
||||||
|
URL: ssh://git@github.com/example/repo
|
||||||
|
Branch: main
|
||||||
|
Revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
|
Status: Last reconciled at 2021-07-20 00:48:16 +0000 UTC
|
||||||
|
Message: Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
75
cmd/flux/testdata/trace/helmrelease.yaml
vendored
Normal file
75
cmd/flux/testdata/trace/helmrelease.yaml
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
---
|
||||||
|
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||||
|
kind: HelmRelease
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
kustomize.toolkit.fluxcd.io/name: infrastructure
|
||||||
|
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||||
|
name: podinfo
|
||||||
|
namespace: podinfo
|
||||||
|
spec:
|
||||||
|
chart:
|
||||||
|
spec:
|
||||||
|
chart: podinfo
|
||||||
|
sourceRef:
|
||||||
|
kind: HelmRepository
|
||||||
|
name: podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2021-07-16T15:42:20Z"
|
||||||
|
message: Release reconciliation succeeded
|
||||||
|
reason: ReconciliationSucceeded
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
||||||
|
helmChart: flux-system/podinfo-podinfo
|
||||||
|
lastAppliedRevision: 6.0.0
|
||||||
|
lastAttemptedRevision: 6.0.0
|
||||||
|
lastAttemptedValuesChecksum: c31db75d05b7515eba2eef47bd71038c74b2e531
|
||||||
|
---
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: infrastructure
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
path: ./infrastructure
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: flux-system
|
||||||
|
validation: client
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2021-08-01T04:52:56Z"
|
||||||
|
message: 'Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||||
|
reason: ReconciliationSucceeded
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
||||||
|
lastAppliedRevision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1beta1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
kustomize.toolkit.fluxcd.io/name: flux-system
|
||||||
|
kustomize.toolkit.fluxcd.io/namespace: flux-system
|
||||||
|
name: flux-system
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
gitImplementation: go-git
|
||||||
|
ref:
|
||||||
|
branch: main
|
||||||
|
secretRef:
|
||||||
|
name: flux-system
|
||||||
|
url: ssh://git@github.com/example/repo
|
||||||
|
status:
|
||||||
|
artifact:
|
||||||
|
lastUpdateTime: "2021-08-01T04:28:42Z"
|
||||||
|
revision: main/696f056df216eea4f9401adbee0ff744d4df390f
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2021-07-20T00:48:16Z"
|
||||||
|
message: 'Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f'
|
||||||
|
reason: GitOperationSucceed
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
||||||
|
|
||||||
492
cmd/flux/trace.go
Normal file
492
cmd/flux/trace.go
Normal file
@@ -0,0 +1,492 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
|
fluxmeta "github.com/fluxcd/pkg/apis/meta"
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var traceCmd = &cobra.Command{
|
||||||
|
Use: "trace [name]",
|
||||||
|
Short: "trace an in-cluster object throughout the GitOps delivery pipeline",
|
||||||
|
Long: `The trace command shows how an object is managed by Flux,
|
||||||
|
from which source and revision it comes, and what's the latest reconciliation status.'`,
|
||||||
|
Example: ` # Trace a Kubernetes Deployment
|
||||||
|
flux trace my-app --kind=deployment --api-version=apps/v1 --namespace=apps
|
||||||
|
|
||||||
|
# Trace a Kubernetes Pod
|
||||||
|
flux trace redis-master-0 --kind=pod --api-version=v1 -n redis
|
||||||
|
|
||||||
|
# Trace a Kubernetes global object
|
||||||
|
flux trace redis --kind=namespace --api-version=v1
|
||||||
|
|
||||||
|
# Trace a Kubernetes custom resource
|
||||||
|
flux trace redis --kind=helmrelease --api-version=helm.toolkit.fluxcd.io/v2beta1 -n redis`,
|
||||||
|
RunE: traceCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
type traceFlags struct {
|
||||||
|
apiVersion string
|
||||||
|
kind string
|
||||||
|
}
|
||||||
|
|
||||||
|
var traceArgs = traceFlags{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
traceCmd.Flags().StringVar(&traceArgs.kind, "kind", "",
|
||||||
|
"the Kubernetes object kind, e.g. Deployment'")
|
||||||
|
traceCmd.Flags().StringVar(&traceArgs.apiVersion, "api-version", "",
|
||||||
|
"the Kubernetes object API version, e.g. 'apps/v1'")
|
||||||
|
rootCmd.AddCommand(traceCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func traceCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("object name is required")
|
||||||
|
}
|
||||||
|
name := args[0]
|
||||||
|
|
||||||
|
if traceArgs.kind == "" {
|
||||||
|
return fmt.Errorf("object kind is required (--kind)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if traceArgs.apiVersion == "" {
|
||||||
|
return fmt.Errorf("object apiVersion is required (--api-version)")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
kubeClient, err := rootCtx.kubeManager.NewClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
gv, err := schema.ParseGroupVersion(traceArgs.apiVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invaild apiVersion: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := &unstructured.Unstructured{}
|
||||||
|
obj.SetGroupVersionKind(schema.GroupVersionKind{
|
||||||
|
Group: gv.Group,
|
||||||
|
Version: gv.Version,
|
||||||
|
Kind: traceArgs.kind,
|
||||||
|
})
|
||||||
|
|
||||||
|
objName := types.NamespacedName{
|
||||||
|
Namespace: rootArgs.namespace,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kubeClient.Get(ctx, objName, obj)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to find object: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ks, ok := isOwnerManagedByFlux(ctx, kubeClient, obj, kustomizev1.GroupVersion.Group); ok {
|
||||||
|
report, err := traceKustomization(ctx, kubeClient, ks, obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rootCmd.Print(report)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if hr, ok := isOwnerManagedByFlux(ctx, kubeClient, obj, helmv2.GroupVersion.Group); ok {
|
||||||
|
report, err := traceHelm(ctx, kubeClient, hr, obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rootCmd.Print(report)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("object not managed by Flux")
|
||||||
|
}
|
||||||
|
|
||||||
|
func traceKustomization(ctx context.Context, kubeClient client.Client, ksName types.NamespacedName, obj *unstructured.Unstructured) (string, error) {
|
||||||
|
ks := &kustomizev1.Kustomization{}
|
||||||
|
ksReady := &metav1.Condition{}
|
||||||
|
err := kubeClient.Get(ctx, ksName, ks)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to find kustomization: %w", err)
|
||||||
|
}
|
||||||
|
ksReady = meta.FindStatusCondition(ks.Status.Conditions, fluxmeta.ReadyCondition)
|
||||||
|
|
||||||
|
var ksRepository *sourcev1.GitRepository
|
||||||
|
var ksRepositoryReady *metav1.Condition
|
||||||
|
if ks.Spec.SourceRef.Kind == sourcev1.GitRepositoryKind {
|
||||||
|
ksRepository = &sourcev1.GitRepository{}
|
||||||
|
sourceNamespace := ks.Namespace
|
||||||
|
if ks.Spec.SourceRef.Namespace != "" {
|
||||||
|
sourceNamespace = ks.Spec.SourceRef.Namespace
|
||||||
|
}
|
||||||
|
err = kubeClient.Get(ctx, types.NamespacedName{
|
||||||
|
Namespace: sourceNamespace,
|
||||||
|
Name: ks.Spec.SourceRef.Name,
|
||||||
|
}, ksRepository)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to find GitRepository: %w", err)
|
||||||
|
}
|
||||||
|
ksRepositoryReady = meta.FindStatusCondition(ksRepository.Status.Conditions, fluxmeta.ReadyCondition)
|
||||||
|
}
|
||||||
|
|
||||||
|
var traceTmpl = `
|
||||||
|
Object: {{.ObjectName}}
|
||||||
|
{{- if .ObjectNamespace }}
|
||||||
|
Namespace: {{.ObjectNamespace}}
|
||||||
|
{{- end }}
|
||||||
|
Status: Managed by Flux
|
||||||
|
{{- if .Kustomization }}
|
||||||
|
---
|
||||||
|
Kustomization: {{.Kustomization.Name}}
|
||||||
|
Namespace: {{.Kustomization.Namespace}}
|
||||||
|
{{- if .Kustomization.Spec.TargetNamespace }}
|
||||||
|
Target: {{.Kustomization.Spec.TargetNamespace}}
|
||||||
|
{{- end }}
|
||||||
|
Path: {{.Kustomization.Spec.Path}}
|
||||||
|
Revision: {{.Kustomization.Status.LastAppliedRevision}}
|
||||||
|
{{- if .KustomizationReady }}
|
||||||
|
Status: Last reconciled at {{.KustomizationReady.LastTransitionTime}}
|
||||||
|
Message: {{.KustomizationReady.Message}}
|
||||||
|
{{- else }}
|
||||||
|
Status: Unknown
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .GitRepository }}
|
||||||
|
---
|
||||||
|
GitRepository: {{.GitRepository.Name}}
|
||||||
|
Namespace: {{.GitRepository.Namespace}}
|
||||||
|
URL: {{.GitRepository.Spec.URL}}
|
||||||
|
{{- if .GitRepository.Spec.Reference }}
|
||||||
|
{{- if .GitRepository.Spec.Reference.Tag }}
|
||||||
|
Tag: {{.GitRepository.Spec.Reference.Tag}}
|
||||||
|
{{- else if .GitRepository.Spec.Reference.SemVer }}
|
||||||
|
Tag: {{.GitRepository.Spec.Reference.SemVer}}
|
||||||
|
{{- else if .GitRepository.Spec.Reference.Branch }}
|
||||||
|
Branch: {{.GitRepository.Spec.Reference.Branch}}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .GitRepository.Status.Artifact }}
|
||||||
|
Revision: {{.GitRepository.Status.Artifact.Revision}}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .GitRepositoryReady }}
|
||||||
|
{{- if eq .GitRepositoryReady.Status "False" }}
|
||||||
|
Status: Last reconciliation failed at {{.GitRepositoryReady.LastTransitionTime}}
|
||||||
|
{{- else }}
|
||||||
|
Status: Last reconciled at {{.GitRepositoryReady.LastTransitionTime}}
|
||||||
|
{{- end }}
|
||||||
|
Message: {{.GitRepositoryReady.Message}}
|
||||||
|
{{- else }}
|
||||||
|
Status: Unknown
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
`
|
||||||
|
|
||||||
|
traceResult := struct {
|
||||||
|
ObjectName string
|
||||||
|
ObjectNamespace string
|
||||||
|
Kustomization *kustomizev1.Kustomization
|
||||||
|
KustomizationReady *metav1.Condition
|
||||||
|
GitRepository *sourcev1.GitRepository
|
||||||
|
GitRepositoryReady *metav1.Condition
|
||||||
|
}{
|
||||||
|
ObjectName: obj.GetKind() + "/" + obj.GetName(),
|
||||||
|
ObjectNamespace: obj.GetNamespace(),
|
||||||
|
Kustomization: ks,
|
||||||
|
KustomizationReady: ksReady,
|
||||||
|
GitRepository: ksRepository,
|
||||||
|
GitRepositoryReady: ksRepositoryReady,
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := template.New("tmpl").Parse(traceTmpl)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data bytes.Buffer
|
||||||
|
writer := bufio.NewWriter(&data)
|
||||||
|
if err := t.Execute(writer, traceResult); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func traceHelm(ctx context.Context, kubeClient client.Client, hrName types.NamespacedName, obj *unstructured.Unstructured) (string, error) {
|
||||||
|
hr := &helmv2.HelmRelease{}
|
||||||
|
hrReady := &metav1.Condition{}
|
||||||
|
err := kubeClient.Get(ctx, hrName, hr)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to find HelmRelease: %w", err)
|
||||||
|
}
|
||||||
|
hrReady = meta.FindStatusCondition(hr.Status.Conditions, fluxmeta.ReadyCondition)
|
||||||
|
|
||||||
|
var hrChart *sourcev1.HelmChart
|
||||||
|
var hrChartReady *metav1.Condition
|
||||||
|
if chart := hr.Status.HelmChart; chart != "" {
|
||||||
|
hrChart = &sourcev1.HelmChart{}
|
||||||
|
err = kubeClient.Get(ctx, utils.ParseNamespacedName(chart), hrChart)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to find HelmChart: %w", err)
|
||||||
|
}
|
||||||
|
hrChartReady = meta.FindStatusCondition(hrChart.Status.Conditions, fluxmeta.ReadyCondition)
|
||||||
|
}
|
||||||
|
|
||||||
|
var hrGitRepository *sourcev1.GitRepository
|
||||||
|
var hrGitRepositoryReady *metav1.Condition
|
||||||
|
if hr.Spec.Chart.Spec.SourceRef.Kind == sourcev1.GitRepositoryKind {
|
||||||
|
hrGitRepository = &sourcev1.GitRepository{}
|
||||||
|
sourceNamespace := hr.Namespace
|
||||||
|
if hr.Spec.Chart.Spec.SourceRef.Namespace != "" {
|
||||||
|
sourceNamespace = hr.Spec.Chart.Spec.SourceRef.Namespace
|
||||||
|
}
|
||||||
|
err = kubeClient.Get(ctx, types.NamespacedName{
|
||||||
|
Namespace: sourceNamespace,
|
||||||
|
Name: hr.Spec.Chart.Spec.SourceRef.Name,
|
||||||
|
}, hrGitRepository)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to find GitRepository: %w", err)
|
||||||
|
}
|
||||||
|
hrGitRepositoryReady = meta.FindStatusCondition(hrGitRepository.Status.Conditions, fluxmeta.ReadyCondition)
|
||||||
|
}
|
||||||
|
|
||||||
|
var hrHelmRepository *sourcev1.HelmRepository
|
||||||
|
var hrHelmRepositoryReady *metav1.Condition
|
||||||
|
if hr.Spec.Chart.Spec.SourceRef.Kind == sourcev1.HelmRepositoryKind {
|
||||||
|
hrHelmRepository = &sourcev1.HelmRepository{}
|
||||||
|
sourceNamespace := hr.Namespace
|
||||||
|
if hr.Spec.Chart.Spec.SourceRef.Namespace != "" {
|
||||||
|
sourceNamespace = hr.Spec.Chart.Spec.SourceRef.Namespace
|
||||||
|
}
|
||||||
|
err = kubeClient.Get(ctx, types.NamespacedName{
|
||||||
|
Namespace: sourceNamespace,
|
||||||
|
Name: hr.Spec.Chart.Spec.SourceRef.Name,
|
||||||
|
}, hrHelmRepository)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to find HelmRepository: %w", err)
|
||||||
|
}
|
||||||
|
hrHelmRepositoryReady = meta.FindStatusCondition(hrHelmRepository.Status.Conditions, fluxmeta.ReadyCondition)
|
||||||
|
}
|
||||||
|
|
||||||
|
var traceTmpl = `
|
||||||
|
Object: {{.ObjectName}}
|
||||||
|
{{- if .ObjectNamespace }}
|
||||||
|
Namespace: {{.ObjectNamespace}}
|
||||||
|
{{- end }}
|
||||||
|
Status: Managed by Flux
|
||||||
|
{{- if .HelmRelease }}
|
||||||
|
---
|
||||||
|
HelmRelease: {{.HelmRelease.Name}}
|
||||||
|
Namespace: {{.HelmRelease.Namespace}}
|
||||||
|
{{- if .HelmRelease.Spec.TargetNamespace }}
|
||||||
|
Target: {{.HelmRelease.Spec.TargetNamespace}}
|
||||||
|
{{- end }}
|
||||||
|
Revision: {{.HelmRelease.Status.LastAppliedRevision}}
|
||||||
|
{{- if .HelmReleaseReady }}
|
||||||
|
Status: Last reconciled at {{.HelmReleaseReady.LastTransitionTime}}
|
||||||
|
Message: {{.HelmReleaseReady.Message}}
|
||||||
|
{{- else }}
|
||||||
|
Status: Unknown
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .HelmChart }}
|
||||||
|
---
|
||||||
|
HelmChart: {{.HelmChart.Name}}
|
||||||
|
Namespace: {{.HelmChart.Namespace}}
|
||||||
|
Chart: {{.HelmChart.Spec.Chart}}
|
||||||
|
Version: {{.HelmChart.Spec.Version}}
|
||||||
|
{{- if .HelmChart.Status.Artifact }}
|
||||||
|
Revision: {{.HelmChart.Status.Artifact.Revision}}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .HelmChartReady }}
|
||||||
|
Status: Last reconciled at {{.HelmChartReady.LastTransitionTime}}
|
||||||
|
Message: {{.HelmChartReady.Message}}
|
||||||
|
{{- else }}
|
||||||
|
Status: Unknown
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .HelmRepository }}
|
||||||
|
---
|
||||||
|
HelmRepository: {{.HelmRepository.Name}}
|
||||||
|
Namespace: {{.HelmRepository.Namespace}}
|
||||||
|
URL: {{.HelmRepository.Spec.URL}}
|
||||||
|
{{- if .HelmRepository.Status.Artifact }}
|
||||||
|
Revision: {{.HelmRepository.Status.Artifact.Revision}}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .HelmRepositoryReady }}
|
||||||
|
Status: Last reconciled at {{.HelmRepositoryReady.LastTransitionTime}}
|
||||||
|
Message: {{.HelmRepositoryReady.Message}}
|
||||||
|
{{- else }}
|
||||||
|
Status: Unknown
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .GitRepository }}
|
||||||
|
---
|
||||||
|
GitRepository: {{.GitRepository.Name}}
|
||||||
|
Namespace: {{.GitRepository.Namespace}}
|
||||||
|
URL: {{.GitRepository.Spec.URL}}
|
||||||
|
{{- if .GitRepository.Spec.Reference }}
|
||||||
|
{{- if .GitRepository.Spec.Reference.Tag }}
|
||||||
|
Tag: {{.GitRepository.Spec.Reference.Tag}}
|
||||||
|
{{- else if .GitRepository.Spec.Reference.SemVer }}
|
||||||
|
Tag: {{.GitRepository.Spec.Reference.SemVer}}
|
||||||
|
{{- else if .GitRepository.Spec.Reference.Branch }}
|
||||||
|
Branch: {{.GitRepository.Spec.Reference.Branch}}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .GitRepository.Status.Artifact }}
|
||||||
|
Revision: {{.GitRepository.Status.Artifact.Revision}}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .GitRepositoryReady }}
|
||||||
|
{{- if eq .GitRepositoryReady.Status "False" }}
|
||||||
|
Status: Last reconciliation failed at {{.GitRepositoryReady.LastTransitionTime}}
|
||||||
|
{{- else }}
|
||||||
|
Status: Last reconciled at {{.GitRepositoryReady.LastTransitionTime}}
|
||||||
|
{{- end }}
|
||||||
|
Message: {{.GitRepositoryReady.Message}}
|
||||||
|
{{- else }}
|
||||||
|
Status: Unknown
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
`
|
||||||
|
|
||||||
|
traceResult := struct {
|
||||||
|
ObjectName string
|
||||||
|
ObjectNamespace string
|
||||||
|
HelmRelease *helmv2.HelmRelease
|
||||||
|
HelmReleaseReady *metav1.Condition
|
||||||
|
HelmChart *sourcev1.HelmChart
|
||||||
|
HelmChartReady *metav1.Condition
|
||||||
|
GitRepository *sourcev1.GitRepository
|
||||||
|
GitRepositoryReady *metav1.Condition
|
||||||
|
HelmRepository *sourcev1.HelmRepository
|
||||||
|
HelmRepositoryReady *metav1.Condition
|
||||||
|
}{
|
||||||
|
ObjectName: obj.GetKind() + "/" + obj.GetName(),
|
||||||
|
ObjectNamespace: obj.GetNamespace(),
|
||||||
|
HelmRelease: hr,
|
||||||
|
HelmReleaseReady: hrReady,
|
||||||
|
HelmChart: hrChart,
|
||||||
|
HelmChartReady: hrChartReady,
|
||||||
|
GitRepository: hrGitRepository,
|
||||||
|
GitRepositoryReady: hrGitRepositoryReady,
|
||||||
|
HelmRepository: hrHelmRepository,
|
||||||
|
HelmRepositoryReady: hrHelmRepositoryReady,
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := template.New("tmpl").Parse(traceTmpl)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var data bytes.Buffer
|
||||||
|
writer := bufio.NewWriter(&data)
|
||||||
|
if err := t.Execute(writer, traceResult); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isManagedByFlux(obj *unstructured.Unstructured, group string) (types.NamespacedName, bool) {
|
||||||
|
nameKey := fmt.Sprintf("%s/name", group)
|
||||||
|
namespaceKey := fmt.Sprintf("%s/namespace", group)
|
||||||
|
namespacedName := types.NamespacedName{}
|
||||||
|
|
||||||
|
for k, v := range obj.GetLabels() {
|
||||||
|
if k == nameKey {
|
||||||
|
namespacedName.Name = v
|
||||||
|
}
|
||||||
|
if k == namespaceKey {
|
||||||
|
namespacedName.Namespace = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if namespacedName.Name == "" {
|
||||||
|
return namespacedName, false
|
||||||
|
}
|
||||||
|
return namespacedName, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isOwnerManagedByFlux(ctx context.Context, kubeClient client.Client, obj *unstructured.Unstructured, group string) (types.NamespacedName, bool) {
|
||||||
|
if n, ok := isManagedByFlux(obj, group); ok {
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
|
|
||||||
|
namespacedName := types.NamespacedName{}
|
||||||
|
for _, reference := range obj.GetOwnerReferences() {
|
||||||
|
owner := &unstructured.Unstructured{}
|
||||||
|
gv, err := schema.ParseGroupVersion(reference.APIVersion)
|
||||||
|
if err != nil {
|
||||||
|
return namespacedName, false
|
||||||
|
}
|
||||||
|
|
||||||
|
owner.SetGroupVersionKind(schema.GroupVersionKind{
|
||||||
|
Group: gv.Group,
|
||||||
|
Version: gv.Version,
|
||||||
|
Kind: reference.Kind,
|
||||||
|
})
|
||||||
|
|
||||||
|
ownerName := types.NamespacedName{
|
||||||
|
Namespace: obj.GetNamespace(),
|
||||||
|
Name: reference.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kubeClient.Get(ctx, ownerName, owner)
|
||||||
|
if err != nil {
|
||||||
|
return namespacedName, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if n, ok := isManagedByFlux(owner, group); ok {
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(owner.GetOwnerReferences()) > 0 {
|
||||||
|
return isOwnerManagedByFlux(ctx, kubeClient, owner, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return namespacedName, false
|
||||||
|
}
|
||||||
44
cmd/flux/trace_test.go
Normal file
44
cmd/flux/trace_test.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTraceNoArgs(t *testing.T) {
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: "trace",
|
||||||
|
wantError: true,
|
||||||
|
goldenValue: "object name is required",
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraceDeployment(t *testing.T) {
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: "trace podinfo -n podinfo --kind deployment --api-version=apps/v1",
|
||||||
|
wantError: false,
|
||||||
|
goldenFile: "testdata/trace/deployment.txt",
|
||||||
|
objectFile: "testdata/trace/deployment.yaml",
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraceHelmRelease(t *testing.T) {
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: "trace podinfo -n podinfo --kind HelmRelease --api-version=helm.toolkit.fluxcd.io/v2beta1",
|
||||||
|
wantError: false,
|
||||||
|
goldenFile: "testdata/trace/helmrelease.txt",
|
||||||
|
objectFile: "testdata/trace/helmrelease.yaml",
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraceHelmReleaseMissingGitRef(t *testing.T) {
|
||||||
|
cmd := cmdTestCase{
|
||||||
|
args: "trace podinfo -n podinfo --kind HelmRelease --api-version=helm.toolkit.fluxcd.io/v2beta1",
|
||||||
|
wantError: false,
|
||||||
|
goldenFile: "testdata/trace/helmrelease-missing-git-ref.txt",
|
||||||
|
objectFile: "testdata/trace/helmrelease-missing-git-ref.yaml",
|
||||||
|
}
|
||||||
|
cmd.runTestCmd(t)
|
||||||
|
}
|
||||||
35
go.mod
35
go.mod
@@ -6,31 +6,32 @@ require (
|
|||||||
github.com/Masterminds/semver/v3 v3.1.0
|
github.com/Masterminds/semver/v3 v3.1.0
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2
|
github.com/cyphar/filepath-securejoin v0.2.2
|
||||||
github.com/fluxcd/go-git-providers v0.1.1
|
github.com/fluxcd/go-git-providers v0.1.1
|
||||||
github.com/fluxcd/helm-controller/api v0.10.1
|
github.com/fluxcd/helm-controller/api v0.11.2
|
||||||
github.com/fluxcd/image-automation-controller/api v0.11.0
|
github.com/fluxcd/image-automation-controller/api v0.14.1
|
||||||
github.com/fluxcd/image-reflector-controller/api v0.9.1
|
github.com/fluxcd/image-reflector-controller/api v0.11.1
|
||||||
github.com/fluxcd/kustomize-controller/api v0.12.2
|
github.com/fluxcd/kustomize-controller/api v0.13.3
|
||||||
github.com/fluxcd/notification-controller/api v0.14.1
|
github.com/fluxcd/notification-controller/api v0.15.1
|
||||||
github.com/fluxcd/pkg/apis/meta v0.9.0
|
github.com/fluxcd/pkg/apis/meta v0.10.0
|
||||||
github.com/fluxcd/pkg/runtime v0.11.0
|
github.com/fluxcd/pkg/runtime v0.12.0
|
||||||
github.com/fluxcd/pkg/ssh v0.0.5
|
github.com/fluxcd/pkg/ssh v0.0.5
|
||||||
github.com/fluxcd/pkg/untar v0.0.5
|
github.com/fluxcd/pkg/untar v0.0.5
|
||||||
github.com/fluxcd/pkg/version v0.0.1
|
github.com/fluxcd/pkg/version v0.0.1
|
||||||
github.com/fluxcd/source-controller/api v0.13.2
|
github.com/fluxcd/source-controller/api v0.15.4
|
||||||
github.com/go-git/go-git/v5 v5.4.2
|
github.com/go-git/go-git/v5 v5.4.2
|
||||||
|
github.com/google/go-cmp v0.5.5
|
||||||
github.com/google/go-containerregistry v0.2.0
|
github.com/google/go-containerregistry v0.2.0
|
||||||
github.com/manifoldco/promptui v0.7.0
|
github.com/manifoldco/promptui v0.7.0
|
||||||
|
github.com/mattn/go-shellwords v1.0.12
|
||||||
github.com/olekukonko/tablewriter v0.0.4
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
github.com/spf13/cobra v1.1.1
|
github.com/spf13/cobra v1.1.3
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
|
||||||
k8s.io/api v0.20.4
|
k8s.io/api v0.21.3
|
||||||
k8s.io/apiextensions-apiserver v0.20.4
|
k8s.io/apiextensions-apiserver v0.21.3
|
||||||
k8s.io/apimachinery v0.20.4
|
k8s.io/apimachinery v0.21.3
|
||||||
k8s.io/cli-runtime v0.20.2 // indirect
|
k8s.io/client-go v0.21.3
|
||||||
k8s.io/client-go v0.20.4
|
sigs.k8s.io/cli-utils v0.25.1-0.20210608181808-f3974341173a
|
||||||
sigs.k8s.io/cli-utils v0.22.2
|
sigs.k8s.io/controller-runtime v0.9.5
|
||||||
sigs.k8s.io/controller-runtime v0.8.3
|
sigs.k8s.io/kustomize/api v0.8.10
|
||||||
sigs.k8s.io/kustomize/api v0.7.4
|
|
||||||
sigs.k8s.io/yaml v1.2.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package bootstrap
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -160,7 +159,7 @@ func (b *PlainGitBootstrapper) ReconcileComponents(ctx context.Context, manifest
|
|||||||
// Apply components using any existing customisations
|
// Apply components using any existing customisations
|
||||||
kfile := filepath.Join(filepath.Dir(componentsYAML), konfig.DefaultKustomizationFileName())
|
kfile := filepath.Join(filepath.Dir(componentsYAML), konfig.DefaultKustomizationFileName())
|
||||||
if _, err := os.Stat(kfile); err == nil {
|
if _, err := os.Stat(kfile); err == nil {
|
||||||
tmpDir, err := ioutil.TempDir("", "gotk-crds")
|
tmpDir, err := os.MkdirTemp("", "gotk-crds")
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
// Extract the CRDs from the components manifest
|
// Extract the CRDs from the components manifest
|
||||||
|
|||||||
69
internal/utils/testdata/components-with-crds.yaml
vendored
Normal file
69
internal/utils/testdata/components-with-crds.yaml
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: flux-system
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
name: alerts.notification.toolkit.fluxcd.io
|
||||||
|
spec:
|
||||||
|
group: notification.toolkit.fluxcd.io
|
||||||
|
names:
|
||||||
|
kind: Alert
|
||||||
|
listKind: AlertList
|
||||||
|
plural: alerts
|
||||||
|
singular: alert
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1beta1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
name: buckets.source.toolkit.fluxcd.io
|
||||||
|
spec:
|
||||||
|
group: source.toolkit.fluxcd.io
|
||||||
|
names:
|
||||||
|
kind: Bucket
|
||||||
|
listKind: BucketList
|
||||||
|
plural: buckets
|
||||||
|
singular: bucket
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1beta1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: kustomize-controller
|
||||||
|
namespace: flux-system
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: notification-controller
|
||||||
|
namespace: flux-system
|
||||||
17
internal/utils/testdata/components-without-crds.yaml
vendored
Normal file
17
internal/utils/testdata/components-without-crds.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: flux-system
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: kustomize-controller
|
||||||
|
namespace: flux-system
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: notification-controller
|
||||||
|
namespace: flux-system
|
||||||
@@ -17,18 +17,15 @@ limitations under the License.
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
@@ -38,6 +35,7 @@ import (
|
|||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
apiruntime "k8s.io/apimachinery/pkg/runtime"
|
apiruntime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
sigyaml "k8s.io/apimachinery/pkg/util/yaml"
|
sigyaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@@ -45,8 +43,8 @@ import (
|
|||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||||
imageautov1 "github.com/fluxcd/image-automation-controller/api/v1alpha2"
|
imageautov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
imagereflectv1 "github.com/fluxcd/image-reflector-controller/api/v1alpha2"
|
imagereflectv1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
|
||||||
"github.com/fluxcd/pkg/runtime/dependency"
|
"github.com/fluxcd/pkg/runtime/dependency"
|
||||||
@@ -109,36 +107,6 @@ func ExecKubectlCommand(ctx context.Context, mode ExecMode, kubeConfigPath strin
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExecTemplate(obj interface{}, tmpl, filename string) error {
|
|
||||||
t, err := template.New("tmpl").Parse(tmpl)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var data bytes.Buffer
|
|
||||||
writer := bufio.NewWriter(&data)
|
|
||||||
if err := t.Execute(writer, obj); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writer.Flush(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := os.Create(filename)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
_, err = io.WriteString(file, data.String())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return file.Sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
func KubeConfig(kubeConfigPath string, kubeContext string) (*rest.Config, error) {
|
func KubeConfig(kubeConfigPath string, kubeContext string) (*rest.Config, error) {
|
||||||
configFiles := SplitKubeConfigPath(kubeConfigPath)
|
configFiles := SplitKubeConfigPath(kubeConfigPath)
|
||||||
configOverrides := clientcmd.ConfigOverrides{}
|
configOverrides := clientcmd.ConfigOverrides{}
|
||||||
@@ -163,12 +131,39 @@ func KubeConfig(kubeConfigPath string, kubeContext string) (*rest.Config, error)
|
|||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func KubeClient(kubeConfigPath string, kubeContext string) (client.Client, error) {
|
// KubeManger creates a Kubernetes client.Client. This interface exists to
|
||||||
|
// facilitate unit testing and provide a fake client.
|
||||||
|
type KubeManager interface {
|
||||||
|
NewClient(string, string) (client.WithWatch, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultKubeManager struct{}
|
||||||
|
|
||||||
|
func DefaultKubeManager() KubeManager {
|
||||||
|
var manager defaultKubeManager
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m defaultKubeManager) NewClient(kubeConfigPath string, kubeContext string) (client.WithWatch, error) {
|
||||||
cfg, err := KubeConfig(kubeConfigPath, kubeContext)
|
cfg, err := KubeConfig(kubeConfigPath, kubeContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scheme := NewScheme()
|
||||||
|
kubeClient, err := client.NewWithWatch(cfg, client.Options{
|
||||||
|
Scheme: scheme,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return kubeClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the Scheme, methods for serializing and deserializing API objects
|
||||||
|
// which can be shared by tests.
|
||||||
|
func NewScheme() *apiruntime.Scheme {
|
||||||
scheme := apiruntime.NewScheme()
|
scheme := apiruntime.NewScheme()
|
||||||
_ = apiextensionsv1.AddToScheme(scheme)
|
_ = apiextensionsv1.AddToScheme(scheme)
|
||||||
_ = corev1.AddToScheme(scheme)
|
_ = corev1.AddToScheme(scheme)
|
||||||
@@ -181,15 +176,13 @@ func KubeClient(kubeConfigPath string, kubeContext string) (client.Client, error
|
|||||||
_ = notificationv1.AddToScheme(scheme)
|
_ = notificationv1.AddToScheme(scheme)
|
||||||
_ = imagereflectv1.AddToScheme(scheme)
|
_ = imagereflectv1.AddToScheme(scheme)
|
||||||
_ = imageautov1.AddToScheme(scheme)
|
_ = imageautov1.AddToScheme(scheme)
|
||||||
|
return scheme
|
||||||
|
}
|
||||||
|
|
||||||
kubeClient, err := client.New(cfg, client.Options{
|
func KubeClient(kubeConfigPath string, kubeContext string) (client.WithWatch, error) {
|
||||||
Scheme: scheme,
|
m := DefaultKubeManager()
|
||||||
})
|
kubeClient, err := m.NewClient(kubeConfigPath, kubeContext)
|
||||||
if err != nil {
|
return kubeClient, err
|
||||||
return nil, fmt.Errorf("kubernetes client initialization failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return kubeClient, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SplitKubeConfigPath splits the given KUBECONFIG path based on the runtime OS
|
// SplitKubeConfigPath splits the given KUBECONFIG path based on the runtime OS
|
||||||
@@ -225,6 +218,21 @@ func ContainsEqualFoldItemString(s []string, e string) (string, bool) {
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseNamespacedName extracts the NamespacedName of a resource
|
||||||
|
// based on the '<namespace>/<name>' format
|
||||||
|
func ParseNamespacedName(input string) types.NamespacedName {
|
||||||
|
parts := strings.Split(input, "/")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
return types.NamespacedName{
|
||||||
|
Namespace: parts[0],
|
||||||
|
Name: parts[1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.NamespacedName{
|
||||||
|
Name: input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ParseObjectKindName extracts the kind and name of a resource
|
// ParseObjectKindName extracts the kind and name of a resource
|
||||||
// based on the '<kind>/<name>' format
|
// based on the '<kind>/<name>' format
|
||||||
func ParseObjectKindName(input string) (kind, name string) {
|
func ParseObjectKindName(input string) (kind, name string) {
|
||||||
@@ -239,11 +247,7 @@ func ParseObjectKindName(input string) (kind, name string) {
|
|||||||
// ParseObjectKindNameNamespace extracts the kind, name and namespace of a resource
|
// ParseObjectKindNameNamespace extracts the kind, name and namespace of a resource
|
||||||
// based on the '<kind>/<name>.<namespace>' format
|
// based on the '<kind>/<name>.<namespace>' format
|
||||||
func ParseObjectKindNameNamespace(input string) (kind, name, namespace string) {
|
func ParseObjectKindNameNamespace(input string) (kind, name, namespace string) {
|
||||||
name = input
|
kind, name = ParseObjectKindName(input)
|
||||||
parts := strings.Split(input, "/")
|
|
||||||
if len(parts) == 2 {
|
|
||||||
kind, name = parts[0], parts[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if nn := strings.Split(name, "."); len(nn) > 1 {
|
if nn := strings.Split(name, "."); len(nn) > 1 {
|
||||||
name = strings.Join(nn[:len(nn)-1], ".")
|
name = strings.Join(nn[:len(nn)-1], ".")
|
||||||
@@ -324,7 +328,7 @@ func CompatibleVersion(binary, target string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExtractCRDs(inManifestPath, outManifestPath string) error {
|
func ExtractCRDs(inManifestPath, outManifestPath string) error {
|
||||||
manifests, err := ioutil.ReadFile(inManifestPath)
|
manifests, err := os.ReadFile(inManifestPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -358,5 +362,5 @@ func ExtractCRDs(inManifestPath, outManifestPath string) error {
|
|||||||
return fmt.Errorf("no CRDs found in %s", inManifestPath)
|
return fmt.Errorf("no CRDs found in %s", inManifestPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ioutil.WriteFile(outManifestPath, []byte(crds), os.ModePerm)
|
return os.WriteFile(outManifestPath, []byte(crds), os.ModePerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,14 @@ limitations under the License.
|
|||||||
|
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/runtime/dependency"
|
||||||
|
)
|
||||||
|
|
||||||
func TestCompatibleVersion(t *testing.T) {
|
func TestCompatibleVersion(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -40,3 +47,103 @@ func TestCompatibleVersion(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseObjectKindNameNamespace(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
wantKind string
|
||||||
|
wantName string
|
||||||
|
wantNamespace string
|
||||||
|
}{
|
||||||
|
{"with kind name namespace", "Kustomization/foo.flux-system", "Kustomization", "foo", "flux-system"},
|
||||||
|
{"without namespace", "Kustomization/foo", "Kustomization", "foo", ""},
|
||||||
|
{"name with dots", "Kustomization/foo.bar.flux-system", "Kustomization", "foo.bar", "flux-system"},
|
||||||
|
{"multiple slashes", "foo/bar/baz", "", "foo/bar/baz", ""},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
gotKind, gotName, gotNamespace := ParseObjectKindNameNamespace(tt.input)
|
||||||
|
if gotKind != tt.wantKind {
|
||||||
|
t.Errorf("kind = %s, want %s", gotKind, tt.wantKind)
|
||||||
|
}
|
||||||
|
if gotName != tt.wantName {
|
||||||
|
t.Errorf("name = %s, want %s", gotName, tt.wantName)
|
||||||
|
}
|
||||||
|
if gotNamespace != tt.wantNamespace {
|
||||||
|
t.Errorf("namespace = %s, want %s", gotNamespace, tt.wantNamespace)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMakeDependsOn(t *testing.T) {
|
||||||
|
input := []string{
|
||||||
|
"someNSA/someNameA",
|
||||||
|
"someNSB/someNameB",
|
||||||
|
"someNameC",
|
||||||
|
"someNSD/",
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
want := []dependency.CrossNamespaceDependencyReference{
|
||||||
|
{Namespace: "someNSA", Name: "someNameA"},
|
||||||
|
{Namespace: "someNSB", Name: "someNameB"},
|
||||||
|
{Namespace: "", Name: "someNameC"},
|
||||||
|
{Namespace: "someNSD", Name: ""},
|
||||||
|
{Namespace: "", Name: ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
got := MakeDependsOn(input)
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("MakeDependsOn() = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateComponents(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{"default and extra components", []string{"source-controller", "image-reflector-controller"}, false},
|
||||||
|
{"unknown components", []string{"some-comp-1", "some-comp-2"}, true},
|
||||||
|
{"mix of default and unknown", []string{"source-controller", "some-comp-1"}, true},
|
||||||
|
{"empty", []string{}, false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := ValidateComponents(tt.input); (err != nil) != tt.expectErr {
|
||||||
|
t.Errorf("ValidateComponents() error = %v, expectErr %v", err, tt.expectErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractCRDs(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
inManifestFile string
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{"with crds", "components-with-crds.yaml", false},
|
||||||
|
{"without crds", "components-without-crds.yaml", true},
|
||||||
|
{"non-existent file", "non-existent-file.yaml", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Create temporary directory to write the result in.
|
||||||
|
dir, err := os.MkdirTemp("", "flux-TestExtractCRDs")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create temporary directory: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
outManifestPath := filepath.Join(dir, "crds.yaml")
|
||||||
|
inManifestPath := filepath.Join("testdata", tt.inManifestFile)
|
||||||
|
if err = ExtractCRDs(inManifestPath, outManifestPath); (err != nil) != tt.expectErr {
|
||||||
|
t.Errorf("ExtractCRDs() error = %v, expectErr %v", err, tt.expectErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/helm-controller/releases/download/v0.10.1/helm-controller.crds.yaml
|
- https://github.com/fluxcd/helm-controller/releases/download/v0.11.2/helm-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/helm-controller/releases/download/v0.10.1/helm-controller.deployment.yaml
|
- https://github.com/fluxcd/helm-controller/releases/download/v0.11.2/helm-controller.deployment.yaml
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.11.0/image-automation-controller.crds.yaml
|
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.14.1/image-automation-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.11.0/image-automation-controller.deployment.yaml
|
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.14.1/image-automation-controller.deployment.yaml
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.9.1/image-reflector-controller.crds.yaml
|
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.11.1/image-reflector-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.9.1/image-reflector-controller.deployment.yaml
|
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.11.1/image-reflector-controller.deployment.yaml
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.12.2/kustomize-controller.crds.yaml
|
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.13.3/kustomize-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.12.2/kustomize-controller.deployment.yaml
|
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.13.3/kustomize-controller.deployment.yaml
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/notification-controller/releases/download/v0.14.1/notification-controller.crds.yaml
|
- https://github.com/fluxcd/notification-controller/releases/download/v0.15.1/notification-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/notification-controller/releases/download/v0.14.1/notification-controller.deployment.yaml
|
- https://github.com/fluxcd/notification-controller/releases/download/v0.15.1/notification-controller.deployment.yaml
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/source-controller/releases/download/v0.13.2/source-controller.crds.yaml
|
- https://github.com/fluxcd/source-controller/releases/download/v0.15.4/source-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/source-controller/releases/download/v0.13.2/source-controller.deployment.yaml
|
- https://github.com/fluxcd/source-controller/releases/download/v0.15.4/source-controller.deployment.yaml
|
||||||
- account.yaml
|
- account.yaml
|
||||||
patchesJson6902:
|
patchesJson6902:
|
||||||
- target:
|
- target:
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- https://github.com/fluxcd/source-controller/releases/download/v0.13.2/source-controller.crds.yaml
|
- https://github.com/fluxcd/source-controller/releases/download/v0.15.4/source-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.12.2/kustomize-controller.crds.yaml
|
- https://github.com/fluxcd/kustomize-controller/releases/download/v0.13.3/kustomize-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/helm-controller/releases/download/v0.10.1/helm-controller.crds.yaml
|
- https://github.com/fluxcd/helm-controller/releases/download/v0.11.2/helm-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/notification-controller/releases/download/v0.14.1/notification-controller.crds.yaml
|
- https://github.com/fluxcd/notification-controller/releases/download/v0.15.1/notification-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.9.1/image-reflector-controller.crds.yaml
|
- https://github.com/fluxcd/image-reflector-controller/releases/download/v0.11.1/image-reflector-controller.crds.yaml
|
||||||
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.11.0/image-automation-controller.crds.yaml
|
- https://github.com/fluxcd/image-automation-controller/releases/download/v0.14.1/image-automation-controller.crds.yaml
|
||||||
|
|||||||
@@ -110,8 +110,8 @@ rules:
|
|||||||
- update
|
- update
|
||||||
- patch
|
- patch
|
||||||
# Lock this down to the specific Secret name (Optional)
|
# Lock this down to the specific Secret name (Optional)
|
||||||
resourceNames:
|
#resourceNames:
|
||||||
- $(KUBE_SECRET) # templated from kustomize vars referencing ConfigMap, also see kustomizeconfig.yaml
|
# - $(KUBE_SECRET) # templated from kustomize vars referencing ConfigMap, also see kustomizeconfig.yaml
|
||||||
---
|
---
|
||||||
kind: RoleBinding
|
kind: RoleBinding
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
|||||||
@@ -102,8 +102,8 @@ rules:
|
|||||||
- update
|
- update
|
||||||
- patch
|
- patch
|
||||||
# # Lock this down to the specific Secret name (Optional)
|
# # Lock this down to the specific Secret name (Optional)
|
||||||
resourceNames:
|
#resourceNames:
|
||||||
- $(KUBE_SECRET) # templated from kustomize vars referencing ConfigMap, also see kustomizeconfig.yaml
|
#- $(KUBE_SECRET) # templated from kustomize vars referencing ConfigMap, also see kustomizeconfig.yaml
|
||||||
---
|
---
|
||||||
kind: RoleBinding
|
kind: RoleBinding
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ spec:
|
|||||||
|
|
||||||
apply-secret() {
|
apply-secret() {
|
||||||
/kbin/kubectl create secret docker-registry "${1}" \
|
/kbin/kubectl create secret docker-registry "${1}" \
|
||||||
--docker-passwrod="${2}" \
|
--docker-password="${2}" \
|
||||||
--docker-username="${3}" \
|
--docker-username="${3}" \
|
||||||
--docker-server="${4}" \
|
--docker-server="${4}" \
|
||||||
--dry-run=client -o=yaml \
|
--dry-run=client -o=yaml \
|
||||||
|
|||||||
@@ -547,7 +547,7 @@
|
|||||||
{
|
{
|
||||||
"expr": "rate(process_cpu_seconds_total{namespace=\"$namespace\",pod=~\".*-controller-.*\"}[1m])",
|
"expr": "rate(process_cpu_seconds_total{namespace=\"$namespace\",pod=~\".*-controller-.*\"}[1m])",
|
||||||
"interval": "",
|
"interval": "",
|
||||||
"legendFormat": "{{kubernetes_pod_name}}",
|
"legendFormat": "{{pod}}",
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -643,7 +643,7 @@
|
|||||||
"expr": "rate(go_memstats_alloc_bytes_total{namespace=\"$namespace\",pod=~\".*-controller-.*\"}[1m])",
|
"expr": "rate(go_memstats_alloc_bytes_total{namespace=\"$namespace\",pod=~\".*-controller-.*\"}[1m])",
|
||||||
"hide": false,
|
"hide": false,
|
||||||
"interval": "",
|
"interval": "",
|
||||||
"legendFormat": "{{kubernetes_pod_name}}",
|
"legendFormat": "{{pod}}",
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -21,4 +21,4 @@ spec:
|
|||||||
- image-automation-controller
|
- image-automation-controller
|
||||||
- image-reflector-controller
|
- image-reflector-controller
|
||||||
podMetricsEndpoints:
|
podMetricsEndpoints:
|
||||||
- targetPort: http-prom
|
- port: http-prom
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@@ -55,7 +54,7 @@ func Generate(options Options, manifestsBase string) (*manifestgen.Manifest, err
|
|||||||
} else {
|
} else {
|
||||||
// download the manifests base from GitHub
|
// download the manifests base from GitHub
|
||||||
if manifestsBase == "" {
|
if manifestsBase == "" {
|
||||||
manifestsBase, err = ioutil.TempDir("", options.Namespace)
|
manifestsBase, err = os.MkdirTemp("", options.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("temp dir error: %w", err)
|
return nil, fmt.Errorf("temp dir error: %w", err)
|
||||||
}
|
}
|
||||||
@@ -78,7 +77,7 @@ func Generate(options Options, manifestsBase string) (*manifestgen.Manifest, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := ioutil.ReadFile(output)
|
content, err := os.ReadFile(output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,16 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
"sigs.k8s.io/kustomize/api/krusty"
|
"sigs.k8s.io/kustomize/api/krusty"
|
||||||
|
kustypes "sigs.k8s.io/kustomize/api/types"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/untar"
|
"github.com/fluxcd/pkg/untar"
|
||||||
)
|
)
|
||||||
@@ -101,19 +102,26 @@ func generate(base string, options Options) error {
|
|||||||
// workaround for kustomize not being able to patch the SA in ClusterRoleBindings
|
// workaround for kustomize not being able to patch the SA in ClusterRoleBindings
|
||||||
defaultNS := MakeDefaultOptions().Namespace
|
defaultNS := MakeDefaultOptions().Namespace
|
||||||
if defaultNS != options.Namespace {
|
if defaultNS != options.Namespace {
|
||||||
rbac, err := ioutil.ReadFile(rbacFile)
|
rbac, err := os.ReadFile(rbacFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("reading rbac file failed: %w", err)
|
return fmt.Errorf("reading rbac file failed: %w", err)
|
||||||
}
|
}
|
||||||
rbac = bytes.ReplaceAll(rbac, []byte(defaultNS), []byte(options.Namespace))
|
rbac = bytes.ReplaceAll(rbac, []byte(defaultNS), []byte(options.Namespace))
|
||||||
if err := ioutil.WriteFile(rbacFile, rbac, os.ModePerm); err != nil {
|
if err := os.WriteFile(rbacFile, rbac, os.ModePerm); err != nil {
|
||||||
return fmt.Errorf("replacing service account namespace in rbac failed: %w", err)
|
return fmt.Errorf("replacing service account namespace in rbac failed: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var kustomizeBuildMutex sync.Mutex
|
||||||
|
|
||||||
func build(base, output string) error {
|
func build(base, output string) error {
|
||||||
|
// TODO(stefan): temporary workaround for concurrent map read and map write bug
|
||||||
|
// https://github.com/kubernetes-sigs/kustomize/issues/3659
|
||||||
|
kustomizeBuildMutex.Lock()
|
||||||
|
defer kustomizeBuildMutex.Unlock()
|
||||||
|
|
||||||
kfile := filepath.Join(base, "kustomization.yaml")
|
kfile := filepath.Join(base, "kustomization.yaml")
|
||||||
|
|
||||||
fs := filesys.MakeFsOnDisk()
|
fs := filesys.MakeFsOnDisk()
|
||||||
@@ -137,10 +145,16 @@ func build(base, output string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opt := krusty.MakeDefaultOptions()
|
buildOptions := &krusty.Options{
|
||||||
opt.DoLegacyResourceSort = true
|
DoLegacyResourceSort: true,
|
||||||
k := krusty.MakeKustomizer(fs, opt)
|
LoadRestrictions: kustypes.LoadRestrictionsNone,
|
||||||
m, err := k.Run(base)
|
AddManagedbyLabel: false,
|
||||||
|
DoPrune: false,
|
||||||
|
PluginConfig: kustypes.DisabledPluginConfig(),
|
||||||
|
}
|
||||||
|
|
||||||
|
k := krusty.MakeKustomizer(buildOptions)
|
||||||
|
m, err := k.Run(fs, base)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,11 @@ limitations under the License.
|
|||||||
package kustomization
|
package kustomization
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
|
||||||
"sigs.k8s.io/kustomize/api/konfig"
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/provider"
|
||||||
kustypes "sigs.k8s.io/kustomize/api/types"
|
kustypes "sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
@@ -35,7 +34,8 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
|
|||||||
|
|
||||||
scan := func(base string) ([]string, error) {
|
scan := func(base string) ([]string, error) {
|
||||||
var paths []string
|
var paths []string
|
||||||
uf := kunstruct.NewKunstructuredFactoryImpl()
|
pvd := provider.NewDefaultDepProvider()
|
||||||
|
rf := pvd.GetResourceFactory()
|
||||||
err := options.FileSystem.Walk(base, func(path string, info os.FileInfo, err error) error {
|
err := options.FileSystem.Walk(base, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -58,7 +58,7 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := uf.SliceFromBytes(fContents); err != nil {
|
if _, err := rf.SliceFromBytes(fContents); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
paths = append(paths, path)
|
paths = append(paths, path)
|
||||||
@@ -112,7 +112,7 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
kd, err := ioutil.ReadFile(abskfile)
|
kd, err := os.ReadFile(abskfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package manifestgen
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ func (m *Manifest) WriteFile(rootDir string) (string, error) {
|
|||||||
return "", fmt.Errorf("unable to create dir, error: %w", err)
|
return "", fmt.Errorf("unable to create dir, error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ioutil.WriteFile(output, []byte(m.Content), os.ModePerm); err != nil {
|
if err := os.WriteFile(output, []byte(m.Content), os.ModePerm); err != nil {
|
||||||
return "", fmt.Errorf("unable to write file, error: %w", err)
|
return "", fmt.Errorf("unable to write file, error: %w", err)
|
||||||
}
|
}
|
||||||
return output, nil
|
return output, nil
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ package sourcesecret
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -62,17 +62,17 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
|
|||||||
|
|
||||||
var caFile []byte
|
var caFile []byte
|
||||||
if options.CAFilePath != "" {
|
if options.CAFilePath != "" {
|
||||||
if caFile, err = ioutil.ReadFile(options.CAFilePath); err != nil {
|
if caFile, err = os.ReadFile(options.CAFilePath); err != nil {
|
||||||
return nil, fmt.Errorf("failed to read CA file: %w", err)
|
return nil, fmt.Errorf("failed to read CA file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var certFile, keyFile []byte
|
var certFile, keyFile []byte
|
||||||
if options.CertFilePath != "" && options.KeyFilePath != "" {
|
if options.CertFilePath != "" && options.KeyFilePath != "" {
|
||||||
if certFile, err = ioutil.ReadFile(options.CertFilePath); err != nil {
|
if certFile, err = os.ReadFile(options.CertFilePath); err != nil {
|
||||||
return nil, fmt.Errorf("failed to read cert file: %w", err)
|
return nil, fmt.Errorf("failed to read cert file: %w", err)
|
||||||
}
|
}
|
||||||
if keyFile, err = ioutil.ReadFile(options.KeyFilePath); err != nil {
|
if keyFile, err = os.ReadFile(options.KeyFilePath); err != nil {
|
||||||
return nil, fmt.Errorf("failed to read key file: %w", err)
|
return nil, fmt.Errorf("failed to read key file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ func buildSecret(keypair *ssh.KeyPair, hostKey, caFile, certFile, keyFile []byte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadKeyPair(path string, password string) (*ssh.KeyPair, error) {
|
func loadKeyPair(path string, password string) (*ssh.KeyPair, error) {
|
||||||
b, err := ioutil.ReadFile(path)
|
b, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open private key file: %w", err)
|
return nil, fmt.Errorf("failed to open private key file: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package sourcesecret
|
package sourcesecret
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -43,8 +42,8 @@ func Test_passwordLoadKeyPair(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
pk, _ := ioutil.ReadFile(tt.privateKeyPath)
|
pk, _ := os.ReadFile(tt.privateKeyPath)
|
||||||
ppk, _ := ioutil.ReadFile(tt.publicKeyPath)
|
ppk, _ := os.ReadFile(tt.publicKeyPath)
|
||||||
|
|
||||||
got, err := loadKeyPair(tt.privateKeyPath, tt.password)
|
got, err := loadKeyPair(tt.privateKeyPath, tt.password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -65,7 +64,7 @@ func Test_passwordLoadKeyPair(t *testing.T) {
|
|||||||
func Test_PasswordlessLoadKeyPair(t *testing.T) {
|
func Test_PasswordlessLoadKeyPair(t *testing.T) {
|
||||||
for algo, privateKey := range testdata.PEMBytes {
|
for algo, privateKey := range testdata.PEMBytes {
|
||||||
t.Run(algo, func(t *testing.T) {
|
t.Run(algo, func(t *testing.T) {
|
||||||
f, err := ioutil.TempFile("", "test-private-key-")
|
f, err := os.CreateTemp("", "test-private-key-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create temporary file. err: %s", err)
|
t.Fatalf("unable to create temporary file. err: %s", err)
|
||||||
}
|
}
|
||||||
@@ -81,7 +80,7 @@ func Test_PasswordlessLoadKeyPair(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pk, _ := ioutil.ReadFile(f.Name())
|
pk, _ := os.ReadFile(f.Name())
|
||||||
if !reflect.DeepEqual(got.PrivateKey, pk) {
|
if !reflect.DeepEqual(got.PrivateKey, pk) {
|
||||||
t.Errorf("PrivateKey %s != %s", got.PrivateKey, string(privateKey))
|
t.Errorf("PrivateKey %s != %s", got.PrivateKey, string(privateKey))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user