1
0
mirror of synced 2026-03-02 11:36:56 +00:00

Compare commits

..

4 Commits

Author SHA1 Message Date
Stefan Prodan
e0bc754ad0 Refer to authorisation model in RFC-0001
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-12-17 11:58:45 +02:00
Michael Bridgen
a67d19317b Explain authorisation model and mechanisms
The multi-tenancy implementations described rely on impersonation and
remote apply; to make this RFC stand by itself, those need to be
explained, along with the authorisation model (how Flux "decides" what
it's allowed to do).

This commit adds a summary of the authorisation model, impersonation,
and remote apply, and rejigs the headings a little to make space.

Signed-off-by: Michael Bridgen <michael@weave.works>
2021-12-17 11:58:39 +02:00
Stefan Prodan
dc7cb189fc Incorporate Michael's suggestions
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-12-17 11:58:39 +02:00
Stefan Prodan
d23d87ac94 Define Flux tenancy models
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2021-12-17 11:58:38 +02:00
224 changed files with 1644 additions and 5315 deletions

View File

@@ -12,7 +12,7 @@ provides=("flux-bin")
conflicts=("flux-bin") conflicts=("flux-bin")
replaces=("flux-cli") replaces=("flux-cli")
depends=("glibc") depends=("glibc")
makedepends=('go>=1.17', 'kustomize>=3.0') makedepends=('go>=1.16', 'kustomize>=3.0')
optdepends=('bash-completion: auto-completion for flux in Bash', optdepends=('bash-completion: auto-completion for flux in Bash',
'zsh-completions: auto-completion for flux in ZSH') 'zsh-completions: auto-completion for flux in ZSH')
source=( source=(
@@ -30,20 +30,12 @@ build() {
export CGO_CXXFLAGS="$CXXFLAGS" export CGO_CXXFLAGS="$CXXFLAGS"
export CGO_CPPFLAGS="$CPPFLAGS" export CGO_CPPFLAGS="$CPPFLAGS"
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw" export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
make cmd/flux/.manifests.done ./manifests/scripts/bundle.sh "${PWD}/manifests" "${PWD}/cmd/flux/manifests"
go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux
} }
check() { check() {
cd "flux2-${pkgver}" cd "flux2-${pkgver}"
case $CARCH in
aarch64)
export ENVTEST_ARCH=arm64
;;
armv6h|armv7h)
export ENVTEST_ARCH=arm
;;
esac
make test make test
} }

View File

@@ -11,7 +11,7 @@ license=("APACHE")
provides=("flux-bin") provides=("flux-bin")
conflicts=("flux-bin") conflicts=("flux-bin")
depends=("glibc") depends=("glibc")
makedepends=('go>=1.17', 'kustomize>=3.0', 'git') makedepends=('go>=1.16', 'kustomize>=3.0')
optdepends=('bash-completion: auto-completion for flux in Bash', optdepends=('bash-completion: auto-completion for flux in Bash',
'zsh-completions: auto-completion for flux in ZSH') 'zsh-completions: auto-completion for flux in ZSH')
source=( source=(
@@ -32,20 +32,12 @@ build() {
export CGO_CXXFLAGS="$CXXFLAGS" export CGO_CXXFLAGS="$CXXFLAGS"
export CGO_CPPFLAGS="$CPPFLAGS" export CGO_CPPFLAGS="$CPPFLAGS"
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw" export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
make cmd/flux/.manifests.done make cmd/flux/manifests
go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux
} }
check() { check() {
cd "flux2" cd "flux2"
case $CARCH in
aarch64)
export ENVTEST_ARCH=arm64
;;
armv6h|armv7h)
export ENVTEST_ARCH=arm
;;
esac
make test make test
} }

View File

@@ -21,9 +21,8 @@ set -eu
KIND_VERSION=0.11.1 KIND_VERSION=0.11.1
KUBECTL_VERSION=1.21.2 KUBECTL_VERSION=1.21.2
KUSTOMIZE_VERSION=4.1.3 KUSTOMIZE_VERSION=4.1.3
HELM_VERSION=3.7.2
GITHUB_RUNNER_VERSION=2.285.1 GITHUB_RUNNER_VERSION=2.285.1
PACKAGES="apt-transport-https ca-certificates software-properties-common build-essential libssl-dev gnupg lsb-release jq pkg-config" PACKAGES="apt-transport-https ca-certificates software-properties-common build-essential libssl-dev gnupg lsb-release jq"
# install prerequisites # install prerequisites
apt-get update \ apt-get update \
@@ -53,12 +52,6 @@ curl -Lo ./kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/release
&& rm kustomize.tar.gz && rm kustomize.tar.gz
install -o root -g root -m 0755 kustomize /usr/local/bin/kustomize install -o root -g root -m 0755 kustomize /usr/local/bin/kustomize
# install helm
curl -Lo ./helm.tar.gz https://get.helm.sh/helm-v${HELM_VERSION}-linux-arm64.tar.gz \
&& tar -zxvf helm.tar.gz \
&& rm helm.tar.gz
install -o root -g root -m 0755 linux-arm64/helm /usr/local/bin/helm
# download runner # 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 \ 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 \ && tar xzf actions-runner-linux-arm64.tar.gz \

View File

@@ -17,13 +17,13 @@ jobs:
uses: actions/cache@v1 uses: actions/cache@v1
with: with:
path: ~/go/pkg/mod path: ~/go/pkg/mod
key: ${{ runner.os }}-go1.17-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go1.17- ${{ runner.os }}-go1.16-
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17.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: with:
@@ -103,26 +103,13 @@ jobs:
/tmp/flux reconcile image repository podinfo /tmp/flux reconcile image repository podinfo
/tmp/flux reconcile image update flux-system /tmp/flux reconcile image update flux-system
/tmp/flux get images all /tmp/flux get images all
/tmp/flux get images policy podinfo | grep "5.2.1"
retries=10 /tmp/flux get image update flux-system | grep commit
count=0
ok=false
until ${ok}; do
/tmp/flux get image update flux-system | grep 'commit' && ok=true || ok=false
count=$(($count + 1))
if [[ ${count} -eq ${retries} ]]; then
echo "No more retries left"
exit 1
fi
sleep 6
/tmp/flux reconcile image update flux-system
done
env: env:
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
GITHUB_REPO_NAME: ${{ steps.vars.outputs.test_repo_name }} GITHUB_REPO_NAME: ${{ steps.vars.outputs.test_repo_name }}
GITHUB_ORG_NAME: fluxcd-testing GITHUB_ORG_NAME: fluxcd-testing
- name: delete repository - name: delete repository
if: ${{ always() }}
run: | run: |
curl \ curl \
-X DELETE \ -X DELETE \

View File

@@ -16,7 +16,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17.x go-version: 1.16.x
- name: Prepare - name: Prepare
id: prep id: prep
run: | run: |

View File

@@ -17,13 +17,13 @@ jobs:
uses: actions/cache@v1 uses: actions/cache@v1
with: with:
path: ~/go/pkg/mod path: ~/go/pkg/mod
key: ${{ runner.os }}-go1.17-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go1.17- ${{ runner.os }}-go1.16-
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17.x go-version: 1.16.x
- name: Install libgit2 - name: Install libgit2
run: | run: |
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 648ACFD622F3D138 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 648ACFD622F3D138

View File

@@ -16,19 +16,23 @@ jobs:
uses: actions/cache@v1 uses: actions/cache@v1
with: with:
path: ~/go/pkg/mod path: ~/go/pkg/mod
key: ${{ runner.os }}-go1.17-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go1.16-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go1.17- ${{ runner.os }}-go1.16-
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17.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: with:
version: v0.11.1 version: v0.11.1
image: kindest/node:v1.20.7 image: kindest/node:v1.19.11@sha256:07db187ae84b4b7de440a73886f008cf903fcf5764ba8106a9fd5243d6f32729
config: .github/kind/config.yaml # disable KIND-net config: .github/kind/config.yaml # disable KIND-net
- name: Setup envtest
uses: fluxcd/pkg/actions/envtest@main
with:
version: "1.21.x"
- name: Setup Calico for network policy - name: Setup Calico for network policy
run: | run: |
kubectl apply -f https://docs.projectcalico.org/v3.20/manifests/calico.yaml kubectl apply -f https://docs.projectcalico.org/v3.20/manifests/calico.yaml

View File

@@ -4,11 +4,6 @@ on:
push: push:
tags: [ 'v*' ] tags: [ 'v*' ]
permissions:
contents: write # needed to write releases
id-token: write # needed for keyless signing
packages: write # needed for ghcr access
jobs: jobs:
goreleaser: goreleaser:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -20,18 +15,16 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17.x go-version: 1.16.x
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v1
with:
platforms: all
- name: Setup Docker Buildx - name: Setup Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v1
- name: Setup Syft with:
uses: anchore/sbom-action/download-syft@v0 buildkitd-flags: "--debug"
- name: Setup Cosign
uses: sigstore/cosign-installer@main
- name: Setup Kustomize
uses: fluxcd/pkg//actions/kustomize@main
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v1 uses: docker/login-action@v1
with: with:
@@ -43,6 +36,18 @@ jobs:
with: with:
username: fluxcdbot username: fluxcdbot
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }} password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
- name: Download release notes utility
env:
GH_REL_URL: https://github.com/buchanae/github-release-notes/releases/download/0.2.0/github-release-notes-linux-amd64-0.2.0.tar.gz
run: cd /tmp && curl -sSL ${GH_REL_URL} | tar xz && sudo mv github-release-notes /usr/local/bin/
- name: Generate release notes
run: |
echo 'CHANGELOG' > /tmp/release.txt
github-release-notes -org fluxcd -repo toolkit -since-latest-release -include-author >> /tmp/release.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Kustomize
uses: fluxcd/pkg//actions/kustomize@main
- name: Generate manifests - name: Generate manifests
run: | run: |
make cmd/flux/.manifests.done make cmd/flux/.manifests.done
@@ -61,22 +66,11 @@ jobs:
- name: Archive the OpenAPI JSON schemas - name: Archive the OpenAPI JSON schemas
run: | run: |
tar -czvf ./output/crd-schemas.tar.gz -C schemas . tar -czvf ./output/crd-schemas.tar.gz -C schemas .
- name: Download release notes utility
env:
GH_REL_URL: https://github.com/buchanae/github-release-notes/releases/download/0.2.0/github-release-notes-linux-amd64-0.2.0.tar.gz
run: cd /tmp && curl -sSL ${GH_REL_URL} | tar xz && sudo mv github-release-notes /usr/local/bin/
- name: Generate release notes
run: |
NOTES="./output/notes.md"
echo '## CLI Changelog' > ${NOTES}
github-release-notes -org fluxcd -repo flux2 -since-latest-release -include-author >> ${NOTES}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run GoReleaser - name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1 uses: goreleaser/goreleaser-action@v1
with: with:
version: latest version: latest
args: release --release-notes=output/notes.md --skip-validate args: release --release-notes=/tmp/release.txt --skip-validate
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}

View File

@@ -16,7 +16,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17.x go-version: 1.16.x
- name: Update component versions - name: Update component versions
id: update id: update
run: | run: |
@@ -42,7 +42,8 @@ jobs:
if [[ "${MOD_VERSION}" != "${LATEST_VERSION}" ]]; then if [[ "${MOD_VERSION}" != "${LATEST_VERSION}" ]]; then
go mod edit -require="github.com/fluxcd/$1/api@${LATEST_VERSION}" go mod edit -require="github.com/fluxcd/$1/api@${LATEST_VERSION}"
make tidy rm go.sum
go mod tidy
changed=true changed=true
fi fi

1
.gitignore vendored
View File

@@ -20,7 +20,6 @@ bin/
output/ output/
cmd/flux/manifests/ cmd/flux/manifests/
cmd/flux/.manifests.done cmd/flux/.manifests.done
testbin/
# Docs # Docs
site/ site/

View File

@@ -40,36 +40,6 @@ archives:
format: zip format: zip
files: files:
- none* - none*
source:
enabled: true
name_template: '{{ .ProjectName }}_{{ .Version }}_source_code'
sboms:
- id: source
artifacts: source
documents:
- "{{ .ProjectName }}_{{ .Version }}_sbom.spdx.json"
release:
extra_files:
- glob: output/crd-schemas.tar.gz
- glob: output/manifests.tar.gz
- glob: output/install.yaml
checksum:
extra_files:
- glob: output/crd-schemas.tar.gz
- glob: output/manifests.tar.gz
- glob: output/install.yaml
signs:
- cmd: cosign
env:
- COSIGN_EXPERIMENTAL=1
certificate: '${artifact}.pem'
args:
- sign-blob
- '--output-certificate=${certificate}'
- '--output-signature=${signature}'
- '${artifact}'
artifacts: checksum
output: true
brews: brews:
- name: flux - name: flux
tap: tap:
@@ -108,12 +78,17 @@ publishers:
- AUR_BOT_SSH_PRIVATE_KEY={{ .Env.AUR_BOT_SSH_PRIVATE_KEY }} - AUR_BOT_SSH_PRIVATE_KEY={{ .Env.AUR_BOT_SSH_PRIVATE_KEY }}
cmd: | cmd: |
.github/aur/flux-go/publish.sh {{ .Version }} .github/aur/flux-go/publish.sh {{ .Version }}
release:
extra_files:
- glob: ./output/crd-schemas.tar.gz
- glob: ./output/manifests.tar.gz
- glob: ./output/install.yaml
dockers: dockers:
- image_templates: - image_templates:
- 'fluxcd/flux-cli:{{ .Tag }}-amd64' - 'fluxcd/flux-cli:{{ .Tag }}-amd64'
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-amd64' - 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-amd64'
dockerfile: Dockerfile dockerfile: Dockerfile
use: buildx use_buildx: true
goos: linux goos: linux
goarch: amd64 goarch: amd64
build_flag_templates: build_flag_templates:
@@ -129,7 +104,7 @@ dockers:
- 'fluxcd/flux-cli:{{ .Tag }}-arm64' - 'fluxcd/flux-cli:{{ .Tag }}-arm64'
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm64' - 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm64'
dockerfile: Dockerfile dockerfile: Dockerfile
use: buildx use_buildx: true
goos: linux goos: linux
goarch: arm64 goarch: arm64
build_flag_templates: build_flag_templates:
@@ -145,7 +120,7 @@ dockers:
- 'fluxcd/flux-cli:{{ .Tag }}-arm' - 'fluxcd/flux-cli:{{ .Tag }}-arm'
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm' - 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm'
dockerfile: Dockerfile dockerfile: Dockerfile
use: buildx use_buildx: true
goos: linux goos: linux
goarch: arm goarch: arm
goarm: 7 goarm: 7
@@ -169,12 +144,3 @@ docker_manifests:
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-amd64' - 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-amd64'
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm64' - 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm64'
- 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm' - 'ghcr.io/fluxcd/flux-cli:{{ .Tag }}-arm'
docker_signs:
- cmd: cosign
env:
- COSIGN_EXPERIMENTAL=1
args:
- sign
- '${artifact}'
artifacts: all
output: true

View File

@@ -67,9 +67,9 @@ for source changes.
Prerequisites: Prerequisites:
* go >= 1.17 * go >= 1.16
* kubectl >= 1.20 * kubectl >= 1.19
* kustomize >= 4.4 * kustomize >= 4.0
Install the [controller-runtime/envtest](https://github.com/kubernetes-sigs/controller-runtime/tree/master/tools/setup-envtest) binaries with: Install the [controller-runtime/envtest](https://github.com/kubernetes-sigs/controller-runtime/tree/master/tools/setup-envtest) binaries with:
@@ -96,25 +96,6 @@ Then you can run the end-to-end tests with:
make e2e make e2e
``` ```
When the output of the Flux CLI changes, to automatically update the golden
files used in the test, pass `-update` flag to the test as:
```bash
make e2e TEST_ARGS="-update"
```
Since not all packages use golden files for testing, `-update` argument must be
passed only for the packages that use golden files. Use the variables
`TEST_PKG_PATH` for unit tests and `E2E_TEST_PKG_PATH` for e2e tests, to set the
path of the target test package:
```bash
# Unit test
make test TEST_PKG_PATH="./cmd/flux" TEST_ARGS="-update"
# e2e test
make e2e E2E_TEST_PKG_PATH="./cmd/flux" TEST_ARGS="-update"
```
Teardown the e2e environment with: Teardown the e2e environment with:
```bash ```bash

View File

@@ -1,15 +1,15 @@
FROM alpine:3.15 as builder FROM alpine:3.14 as builder
RUN apk add --no-cache ca-certificates curl RUN apk add --no-cache ca-certificates curl
ARG ARCH=linux/amd64 ARG ARCH=linux/amd64
ARG KUBECTL_VER=1.23.1 ARG KUBECTL_VER=1.22.2
RUN curl -sL https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/${ARCH}/kubectl \ RUN curl -sL https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/${ARCH}/kubectl \
-o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl && \ -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl && \
kubectl version --client=true kubectl version --client=true
FROM alpine:3.15 as flux-cli FROM alpine:3.14 as flux-cli
# Create minimal nsswitch.conf file to prioritize the usage of /etc/hosts over DNS queries. # Create minimal nsswitch.conf file to prioritize the usage of /etc/hosts over DNS queries.
# https://github.com/gliderlabs/docker-alpine/issues/367#issuecomment-354316460 # https://github.com/gliderlabs/docker-alpine/issues/367#issuecomment-354316460
@@ -20,5 +20,4 @@ RUN apk add --no-cache ca-certificates
COPY --from=builder /usr/local/bin/kubectl /usr/local/bin/ COPY --from=builder /usr/local/bin/kubectl /usr/local/bin/
COPY --chmod=755 flux /usr/local/bin/ COPY --chmod=755 flux /usr/local/bin/
USER 65534:65534
ENTRYPOINT [ "flux" ] ENTRYPOINT [ "flux" ]

View File

@@ -1,8 +1,8 @@
VERSION?=$(shell grep 'VERSION' cmd/flux/main.go | awk '{ print $$4 }' | head -n 1 | tr -d '"') VERSION?=$(shell grep 'VERSION' cmd/flux/main.go | awk '{ print $$4 }' | head -n 1 | tr -d '"')
EMBEDDED_MANIFESTS_TARGET=cmd/flux/.manifests.done EMBEDDED_MANIFESTS_TARGET=cmd/flux/.manifests.done
TEST_KUBECONFIG?=/tmp/flux-e2e-test-kubeconfig TEST_KUBECONFIG?=/tmp/flux-e2e-test-kubeconfig
# Architecture to use envtest with ENVTEST_BIN_VERSION?=latest
ENVTEST_ARCH ?= amd64 KUBEBUILDER_ASSETS?=$(shell $(SETUP_ENVTEST) use -i $(ENVTEST_BIN_VERSION) -p path)
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN)) ifeq (,$(shell go env GOBIN))
@@ -16,8 +16,7 @@ rwildcard=$(foreach d,$(wildcard $(addsuffix *,$(1))),$(call rwildcard,$(d)/,$(2
all: test build all: test build
tidy: tidy:
go mod tidy -compat=1.17 go mod tidy
cd tests/azure && go mod tidy -compat=1.17
fmt: fmt:
go fmt ./... go fmt ./...
@@ -34,14 +33,11 @@ cleanup-kind:
kind delete cluster --name=flux-e2e-test kind delete cluster --name=flux-e2e-test
rm $(TEST_KUBECONFIG) rm $(TEST_KUBECONFIG)
KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_KUBERNETES_VERSION) --bin-dir=$(ENVTEST_ASSETS_DIR) -p path)"
TEST_PKG_PATH="./..."
test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet install-envtest test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet install-envtest
KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test $(TEST_PKG_PATH) -coverprofile cover.out --tags=unit $(TEST_ARGS) KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test ./... -coverprofile cover.out --tags=unit
E2E_TEST_PKG_PATH="./cmd/flux/..."
e2e: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet e2e: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet
TEST_KUBECONFIG=$(TEST_KUBECONFIG) go test $(E2E_TEST_PKG_PATH) -coverprofile e2e.cover.out --tags=e2e -v -failfast $(TEST_ARGS) TEST_KUBECONFIG=$(TEST_KUBECONFIG) go test ./cmd/flux/... -coverprofile e2e.cover.out --tags=e2e -v -failfast
test-with-kind: install-envtest test-with-kind: install-envtest
make setup-kind make setup-kind
@@ -62,33 +58,27 @@ install:
install-dev: install-dev:
CGO_ENABLED=0 go build -o /usr/local/bin ./cmd/flux CGO_ENABLED=0 go build -o /usr/local/bin ./cmd/flux
install-envtest: setup-envtest
$(SETUP_ENVTEST) use $(ENVTEST_BIN_VERSION)
setup-bootstrap-patch: setup-bootstrap-patch:
go run ./tests/bootstrap/main.go go run ./tests/bootstrap/main.go
setup-image-automation: setup-image-automation:
cd tests/image-automation && go run main.go cd tests/image-automation && go run main.go
ENVTEST_ASSETS_DIR=$(shell pwd)/testbin # Find or download setup-envtest
ENVTEST_KUBERNETES_VERSION?=latest setup-envtest:
install-envtest: setup-envtest ifeq (, $(shell which setup-envtest))
mkdir -p ${ENVTEST_ASSETS_DIR} @{ \
$(ENVTEST) use $(ENVTEST_KUBERNETES_VERSION) --arch=$(ENVTEST_ARCH) --bin-dir=$(ENVTEST_ASSETS_DIR) set -e ;\
SETUP_ENVTEST_TMP_DIR=$$(mktemp -d) ;\
ENVTEST = $(shell pwd)/bin/setup-envtest cd $$SETUP_ENVTEST_TMP_DIR ;\
.PHONY: envtest go mod init tmp ;\
setup-envtest: ## Download envtest-setup locally if necessary. go get sigs.k8s.io/controller-runtime/tools/setup-envtest@latest ;\
$(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) rm -rf $$SETUP_ENVTEST_TMP_DIR ;\
}
# go-install-tool will 'go install' any package $2 and install it to $1. SETUP_ENVTEST=$(GOBIN)/setup-envtest
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) else
define go-install-tool SETUP_ENVTEST=$(shell which setup-envtest)
@[ -f $(1) ] || { \ endif
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef

View File

@@ -12,9 +12,6 @@ inputs:
description: "arch can be amd64, arm64 or arm" description: "arch can be amd64, arm64 or arm"
required: true required: true
default: "amd64" default: "amd64"
bindir:
description: "Optional location of the Flux binary. Will not use sudo if set. Updates System Path."
required: false
runs: runs:
using: composite using: composite
steps: steps:
@@ -32,16 +29,10 @@ runs:
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
- name: "Copy Flux binary to execute location" - name: "Add flux binary to /usr/local/bin"
shell: bash shell: bash
run: | run: |
BINDIR=${{ inputs.bindir }} sudo cp /tmp/flux/flux /usr/local/bin
if [ -z $BINDIR ]; then
sudo cp /tmp/flux/flux /usr/local/bin
else
cp /tmp/flux/flux "${BINDIR}"
echo "${BINDIR}" >> $GITHUB_PATH
fi
- name: "Cleanup tmp" - name: "Cleanup tmp"
shell: bash shell: bash
run: | run: |

View File

@@ -25,9 +25,8 @@ import (
// notificationv1.Alert // notificationv1.Alert
var alertType = apiType{ var alertType = apiType{
kind: notificationv1.AlertKind, kind: notificationv1.AlertKind,
humanKind: "alert", humanKind: "alert",
groupVersion: notificationv1.GroupVersion,
} }
type alertAdapter struct { type alertAdapter struct {

View File

@@ -25,9 +25,8 @@ import (
// notificationv1.Provider // notificationv1.Provider
var alertProviderType = apiType{ var alertProviderType = apiType{
kind: notificationv1.ProviderKind, kind: notificationv1.ProviderKind,
humanKind: "alert provider", humanKind: "alert provider",
groupVersion: notificationv1.GroupVersion,
} }
type alertProviderAdapter struct { type alertProviderAdapter struct {

View File

@@ -88,7 +88,7 @@ func init() {
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components, bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.defaultComponents, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values") "list of components, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil, bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.extraComponents, "components-extra", nil,
"list of components in addition to those supplied or defaulted, accepts values such as 'image-reflector-controller,image-automation-controller'") "list of components in addition to those supplied or defaulted, accepts comma-separated values")
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd", bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
"container registry where the toolkit images are published") "container registry where the toolkit images are published")

View File

@@ -121,7 +121,7 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
@@ -179,7 +179,7 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
installOptions := install.Options{ installOptions := install.Options{
BaseURL: rootArgs.defaults.BaseURL, BaseURL: rootArgs.defaults.BaseURL,
Version: bootstrapArgs.version, Version: bootstrapArgs.version,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Components: bootstrapComponents(), Components: bootstrapComponents(),
Registry: bootstrapArgs.registry, Registry: bootstrapArgs.registry,
ImagePullSecret: bootstrapArgs.imagePullSecret, ImagePullSecret: bootstrapArgs.imagePullSecret,
@@ -200,7 +200,7 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
// Source generation and secret config // Source generation and secret config
secretOpts := sourcesecret.Options{ secretOpts := sourcesecret.Options{
Name: bootstrapArgs.secretName, Name: bootstrapArgs.secretName,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
TargetPath: bServerArgs.path.String(), TargetPath: bServerArgs.path.String(),
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile, ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
@@ -232,8 +232,8 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
// Sync manifest config // Sync manifest config
syncOpts := sync.Options{ syncOpts := sync.Options{
Interval: bServerArgs.interval, Interval: bServerArgs.interval,
Name: *kubeconfigArgs.Namespace, Name: rootArgs.namespace,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Branch: bootstrapArgs.branch, Branch: bootstrapArgs.branch,
Secret: bootstrapArgs.secretName, Secret: bootstrapArgs.secretName,
TargetPath: bServerArgs.path.ToSlash(), TargetPath: bServerArgs.path.ToSlash(),
@@ -251,10 +251,9 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix), bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
bootstrap.WithProviderTeamPermissions(mapTeamSlice(bServerArgs.teams, bServerDefaultPermission)), bootstrap.WithProviderTeamPermissions(mapTeamSlice(bServerArgs.teams, bServerDefaultPermission)),
bootstrap.WithReadWriteKeyPermissions(bServerArgs.readWriteKey), bootstrap.WithReadWriteKeyPermissions(bServerArgs.readWriteKey),
bootstrap.WithKubeconfig(kubeconfigArgs), bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext),
bootstrap.WithLogger(logger), bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle), bootstrap.WithCABundle(caBundle),
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
} }
if bootstrapArgs.sshHostname != "" { if bootstrapArgs.sshHostname != "" {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname)) bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))

View File

@@ -101,7 +101,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
@@ -128,7 +128,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
installOptions := install.Options{ installOptions := install.Options{
BaseURL: rootArgs.defaults.BaseURL, BaseURL: rootArgs.defaults.BaseURL,
Version: bootstrapArgs.version, Version: bootstrapArgs.version,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Components: bootstrapComponents(), Components: bootstrapComponents(),
Registry: bootstrapArgs.registry, Registry: bootstrapArgs.registry,
ImagePullSecret: bootstrapArgs.imagePullSecret, ImagePullSecret: bootstrapArgs.imagePullSecret,
@@ -149,7 +149,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
// Source generation and secret config // Source generation and secret config
secretOpts := sourcesecret.Options{ secretOpts := sourcesecret.Options{
Name: bootstrapArgs.secretName, Name: bootstrapArgs.secretName,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
TargetPath: gitArgs.path.String(), TargetPath: gitArgs.path.String(),
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile, ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
@@ -161,15 +161,10 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
secretOpts.CAFilePath = bootstrapArgs.caFile secretOpts.CAFilePath = bootstrapArgs.caFile
} }
// Remove port of the given host when not syncing over HTTP/S to not assume port for protocol
// This _might_ be overwritten later on by e.g. --ssh-hostname
if repositoryURL.Scheme != "https" && repositoryURL.Scheme != "http" {
repositoryURL.Host = repositoryURL.Hostname()
}
// Configure repository URL to match auth config for sync. // Configure repository URL to match auth config for sync.
repositoryURL.User = nil repositoryURL.User = nil
repositoryURL.Scheme = "https" repositoryURL.Scheme = "https"
repositoryURL.Host = repositoryURL.Hostname()
} else { } else {
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm) secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
secretOpts.Password = gitArgs.password secretOpts.Password = gitArgs.password
@@ -199,8 +194,8 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
// Sync manifest config // Sync manifest config
syncOpts := sync.Options{ syncOpts := sync.Options{
Interval: gitArgs.interval, Interval: gitArgs.interval,
Name: *kubeconfigArgs.Namespace, Name: rootArgs.namespace,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
URL: repositoryURL.String(), URL: repositoryURL.String(),
Branch: bootstrapArgs.branch, Branch: bootstrapArgs.branch,
Secret: bootstrapArgs.secretName, Secret: bootstrapArgs.secretName,
@@ -225,7 +220,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithBranch(bootstrapArgs.branch), bootstrap.WithBranch(bootstrapArgs.branch),
bootstrap.WithAuthor(bootstrapArgs.authorName, bootstrapArgs.authorEmail), bootstrap.WithAuthor(bootstrapArgs.authorName, bootstrapArgs.authorEmail),
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix), bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
bootstrap.WithKubeconfig(kubeconfigArgs), bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext),
bootstrap.WithPostGenerateSecretFunc(promptPublicKey), bootstrap.WithPostGenerateSecretFunc(promptPublicKey),
bootstrap.WithLogger(logger), bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle), bootstrap.WithCABundle(caBundle),

View File

@@ -125,7 +125,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
@@ -175,7 +175,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
installOptions := install.Options{ installOptions := install.Options{
BaseURL: rootArgs.defaults.BaseURL, BaseURL: rootArgs.defaults.BaseURL,
Version: bootstrapArgs.version, Version: bootstrapArgs.version,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Components: bootstrapComponents(), Components: bootstrapComponents(),
Registry: bootstrapArgs.registry, Registry: bootstrapArgs.registry,
ImagePullSecret: bootstrapArgs.imagePullSecret, ImagePullSecret: bootstrapArgs.imagePullSecret,
@@ -196,7 +196,7 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
// Source generation and secret config // Source generation and secret config
secretOpts := sourcesecret.Options{ secretOpts := sourcesecret.Options{
Name: bootstrapArgs.secretName, Name: bootstrapArgs.secretName,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
TargetPath: githubArgs.path.ToSlash(), TargetPath: githubArgs.path.ToSlash(),
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile, ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
@@ -221,8 +221,8 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
// Sync manifest config // Sync manifest config
syncOpts := sync.Options{ syncOpts := sync.Options{
Interval: githubArgs.interval, Interval: githubArgs.interval,
Name: *kubeconfigArgs.Namespace, Name: rootArgs.namespace,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Branch: bootstrapArgs.branch, Branch: bootstrapArgs.branch,
Secret: bootstrapArgs.secretName, Secret: bootstrapArgs.secretName,
TargetPath: githubArgs.path.ToSlash(), TargetPath: githubArgs.path.ToSlash(),
@@ -240,10 +240,9 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix), bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
bootstrap.WithProviderTeamPermissions(mapTeamSlice(githubArgs.teams, ghDefaultPermission)), bootstrap.WithProviderTeamPermissions(mapTeamSlice(githubArgs.teams, ghDefaultPermission)),
bootstrap.WithReadWriteKeyPermissions(githubArgs.readWriteKey), bootstrap.WithReadWriteKeyPermissions(githubArgs.readWriteKey),
bootstrap.WithKubeconfig(kubeconfigArgs), bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext),
bootstrap.WithLogger(logger), bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle), bootstrap.WithCABundle(caBundle),
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
} }
if bootstrapArgs.sshHostname != "" { if bootstrapArgs.sshHostname != "" {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname)) bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))

View File

@@ -129,7 +129,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
@@ -186,7 +186,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
installOptions := install.Options{ installOptions := install.Options{
BaseURL: rootArgs.defaults.BaseURL, BaseURL: rootArgs.defaults.BaseURL,
Version: bootstrapArgs.version, Version: bootstrapArgs.version,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Components: bootstrapComponents(), Components: bootstrapComponents(),
Registry: bootstrapArgs.registry, Registry: bootstrapArgs.registry,
ImagePullSecret: bootstrapArgs.imagePullSecret, ImagePullSecret: bootstrapArgs.imagePullSecret,
@@ -207,7 +207,7 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
// Source generation and secret config // Source generation and secret config
secretOpts := sourcesecret.Options{ secretOpts := sourcesecret.Options{
Name: bootstrapArgs.secretName, Name: bootstrapArgs.secretName,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
TargetPath: gitlabArgs.path.String(), TargetPath: gitlabArgs.path.String(),
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile, ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
@@ -235,8 +235,8 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
// Sync manifest config // Sync manifest config
syncOpts := sync.Options{ syncOpts := sync.Options{
Interval: gitlabArgs.interval, Interval: gitlabArgs.interval,
Name: *kubeconfigArgs.Namespace, Name: rootArgs.namespace,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Branch: bootstrapArgs.branch, Branch: bootstrapArgs.branch,
Secret: bootstrapArgs.secretName, Secret: bootstrapArgs.secretName,
TargetPath: gitlabArgs.path.ToSlash(), TargetPath: gitlabArgs.path.ToSlash(),
@@ -254,10 +254,9 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix), bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
bootstrap.WithProviderTeamPermissions(mapTeamSlice(gitlabArgs.teams, glDefaultPermission)), bootstrap.WithProviderTeamPermissions(mapTeamSlice(gitlabArgs.teams, glDefaultPermission)),
bootstrap.WithReadWriteKeyPermissions(gitlabArgs.readWriteKey), bootstrap.WithReadWriteKeyPermissions(gitlabArgs.readWriteKey),
bootstrap.WithKubeconfig(kubeconfigArgs), bootstrap.WithKubeconfig(rootArgs.kubeconfig, rootArgs.kubecontext),
bootstrap.WithLogger(logger), bootstrap.WithLogger(logger),
bootstrap.WithCABundle(caBundle), bootstrap.WithCABundle(caBundle),
bootstrap.WithGitCommitSigning(bootstrapArgs.gpgKeyRingPath, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
} }
if bootstrapArgs.sshHostname != "" { if bootstrapArgs.sshHostname != "" {
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname)) bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))

View File

@@ -1,31 +0,0 @@
/*
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 (
"github.com/spf13/cobra"
)
var buildCmd = &cobra.Command{
Use: "build",
Short: "Build a flux resource",
Long: "The build command is used to build flux resources.",
}
func init() {
rootCmd.AddCommand(buildCmd)
}

View File

@@ -1,100 +0,0 @@
/*
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 (
"fmt"
"os"
"os/signal"
"github.com/spf13/cobra"
"github.com/fluxcd/flux2/internal/build"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
)
var buildKsCmd = &cobra.Command{
Use: "kustomization",
Aliases: []string{"ks"},
Short: "Build Kustomization",
Long: `The build command queries the Kubernetes API and fetches the specified Flux Kustomization.
It then uses the fetched in cluster flux kustomization to perform needed transformation on the local kustomization.yaml
pointed at by --path. The local kustomization.yaml is generated if it does not exist. Finally it builds the overlays using the local kustomization.yaml, and write the resulting multi-doc YAML to stdout.`,
Example: `# Build the local manifests as they were built on the cluster
flux build kustomization my-app --path ./path/to/local/manifests`,
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
RunE: buildKsCmdRun,
}
type buildKsFlags struct {
path string
}
var buildKsArgs buildKsFlags
func init() {
buildKsCmd.Flags().StringVar(&buildKsArgs.path, "path", "", "Path to the manifests location.)")
buildCmd.AddCommand(buildKsCmd)
}
func buildKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("%s name is required", kustomizationType.humanKind)
}
name := args[0]
if buildKsArgs.path == "" {
return fmt.Errorf("invalid resource path %q", buildKsArgs.path)
}
if fs, err := os.Stat(buildKsArgs.path); err != nil || !fs.IsDir() {
return fmt.Errorf("invalid resource path %q", buildKsArgs.path)
}
builder, err := build.NewBuilder(kubeconfigArgs, name, buildKsArgs.path, build.WithTimeout(rootArgs.timeout))
if err != nil {
return err
}
// create a signal channel
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt)
errChan := make(chan error)
go func() {
manifests, err := builder.Build()
if err != nil {
errChan <- err
}
cmd.Print(string(manifests))
errChan <- nil
}()
select {
case <-sigc:
fmt.Println("Build cancelled... exiting.")
return builder.Cancel()
case err := <-errChan:
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,83 +0,0 @@
//go:build unit
// +build unit
/*
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 (
"testing"
)
func setup(t *testing.T, tmpl map[string]string) {
t.Helper()
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-source.yaml", tmpl, t)
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-kustomization.yaml", tmpl, t)
}
func TestBuildKustomization(t *testing.T) {
tests := []struct {
name string
args string
resultFile string
assertFunc string
}{
{
name: "no args",
args: "build kustomization podinfo",
resultFile: "invalid resource path \"\"",
assertFunc: "assertError",
},
{
name: "build podinfo",
args: "build kustomization podinfo --path ./testdata/build-kustomization/podinfo",
resultFile: "./testdata/build-kustomization/podinfo-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
{
name: "build podinfo without service",
args: "build kustomization podinfo --path ./testdata/build-kustomization/delete-service",
resultFile: "./testdata/build-kustomization/podinfo-without-service-result.yaml",
assertFunc: "assertGoldenTemplateFile",
},
}
tmpl := map[string]string{
"fluxns": allocateNamespace("flux-system"),
}
setup(t, tmpl)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var assert assertFunc
switch tt.assertFunc {
case "assertGoldenTemplateFile":
assert = assertGoldenTemplateFile(tt.resultFile, tmpl)
case "assertError":
assert = assertError(tt.resultFile)
}
cmd := cmdTestCase{
args: tt.args + " -n " + tmpl["fluxns"],
assert: assert,
}
cmd.runTestCmd(t)
})
}
}

View File

@@ -56,7 +56,10 @@ type checkFlags struct {
} }
var kubernetesConstraints = []string{ var kubernetesConstraints = []string{
">=1.20.6-0", ">=1.19.0-0",
">=1.16.11-0 <=1.16.15-0",
">=1.17.7-0 <=1.17.17-0",
">=1.18.4-0 <=1.18.20-0",
} }
var checkArgs checkFlags var checkArgs checkFlags
@@ -125,7 +128,7 @@ func fluxCheck() {
} }
func kubernetesCheck(constraints []string) bool { func kubernetesCheck(constraints []string) bool {
cfg, err := utils.KubeConfig(kubeconfigArgs) cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
logger.Failuref("Kubernetes client initialization failed: %s", err.Error()) logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
return false return false
@@ -173,7 +176,7 @@ func componentsCheck() bool {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeConfig, err := utils.KubeConfig(kubeconfigArgs) kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return false return false
} }
@@ -183,7 +186,7 @@ func componentsCheck() bool {
return false return false
} }
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return false return false
} }
@@ -191,7 +194,7 @@ func componentsCheck() bool {
ok := true ok := true
selector := client.MatchingLabels{manifestgen.PartOfLabelKey: manifestgen.PartOfLabelValue} selector := client.MatchingLabels{manifestgen.PartOfLabelKey: manifestgen.PartOfLabelValue}
var list v1.DeploymentList var list v1.DeploymentList
if err := kubeClient.List(ctx, &list, client.InNamespace(*kubeconfigArgs.Namespace), selector); err == nil { if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace), selector); err == nil {
for _, d := range list.Items { for _, d := range list.Items {
if ref, err := buildComponentObjectRefs(d.Name); err == nil { if ref, err := buildComponentObjectRefs(d.Name); err == nil {
if err := statusChecker.Assess(ref...); err != nil { if err := statusChecker.Assess(ref...); err != nil {

View File

@@ -1,4 +1,3 @@
//go:build e2e
// +build e2e // +build e2e
/* /*
@@ -30,7 +29,7 @@ import (
) )
func TestCheckPre(t *testing.T) { func TestCheckPre(t *testing.T) {
jsonOutput, err := utils.ExecKubectlCommand(context.TODO(), utils.ModeCapture, *kubeconfigArgs.KubeConfig, *kubeconfigArgs.Context, "version", "--output", "json") jsonOutput, err := utils.ExecKubectlCommand(context.TODO(), utils.ModeCapture, rootArgs.kubeconfig, rootArgs.kubecontext, "version", "--output", "json")
if err != nil { if err != nil {
t.Fatalf("Error running utils.ExecKubectlCommand: %v", err.Error()) t.Fatalf("Error running utils.ExecKubectlCommand: %v", err.Error())
} }

View File

@@ -25,7 +25,10 @@ import (
"k8s.io/apimachinery/pkg/api/meta" "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/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
memory "k8s.io/client-go/discovery/cached"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/client-go/restmapper"
) )
var completionCmd = &cobra.Command{ var completionCmd = &cobra.Command{
@@ -39,7 +42,7 @@ func init() {
} }
func contextsCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func contextsCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
rawConfig, err := kubeconfigArgs.ToRawKubeConfigLoader().RawConfig() rawConfig, err := utils.ClientConfig(rootArgs.kubeconfig, rootArgs.kubecontext).RawConfig()
if err != nil { if err != nil {
return completionError(err) return completionError(err)
} }
@@ -60,15 +63,16 @@ func resourceNamesCompletionFunc(gvk schema.GroupVersionKind) func(cmd *cobra.Co
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
cfg, err := utils.KubeConfig(kubeconfigArgs) cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return completionError(err) return completionError(err)
} }
mapper, err := kubeconfigArgs.ToRESTMapper() dc, err := discovery.NewDiscoveryClientForConfig(cfg)
if err != nil { if err != nil {
return completionError(err) return completionError(err)
} }
mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(dc))
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version) mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil { if err != nil {
@@ -82,7 +86,7 @@ func resourceNamesCompletionFunc(gvk schema.GroupVersionKind) func(cmd *cobra.Co
var dr dynamic.ResourceInterface var dr dynamic.ResourceInterface
if mapping.Scope.Name() == meta.RESTScopeNameNamespace { if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
dr = client.Resource(mapping.Resource).Namespace(*kubeconfigArgs.Namespace) dr = client.Resource(mapping.Resource).Namespace(rootArgs.namespace)
} else { } else {
dr = client.Resource(mapping.Resource) dr = client.Resource(mapping.Resource)
} }

View File

@@ -19,7 +19,6 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"regexp"
"strings" "strings"
"time" "time"
@@ -52,18 +51,6 @@ func init() {
createCmd.PersistentFlags().BoolVar(&createArgs.export, "export", false, "export in YAML format to stdout") createCmd.PersistentFlags().BoolVar(&createArgs.export, "export", false, "export in YAML format to stdout")
createCmd.PersistentFlags().StringSliceVar(&createArgs.labels, "label", nil, createCmd.PersistentFlags().StringSliceVar(&createArgs.labels, "label", nil,
"set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)") "set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)")
createCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("name is required")
}
name := args[0]
if !validateObjectName(name) {
return fmt.Errorf("name '%s' is invalid, it should adhere to standard defined in RFC 1123, the name can only contain alphanumeric characters or '-'", name)
}
return nil
}
rootCmd.AddCommand(createCmd) rootCmd.AddCommand(createCmd)
} }
@@ -117,7 +104,7 @@ func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) e
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) // NB globals kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext) // NB globals
if err != nil { if err != nil {
return err return err
} }
@@ -163,8 +150,3 @@ func parseLabels() (map[string]string, error) {
return result, nil return result, nil
} }
func validateObjectName(name string) bool {
r := regexp.MustCompile("^[a-z0-9]([a-z0-9\\-]){0,61}[a-z0-9]$")
return r.MatchString(name)
}

View File

@@ -63,6 +63,9 @@ func init() {
} }
func createAlertCmdRun(cmd *cobra.Command, args []string) error { func createAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Alert name is required")
}
name := args[0] name := args[0]
if alertArgs.providerRef == "" { if alertArgs.providerRef == "" {
@@ -99,7 +102,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
alert := notificationv1.Alert{ alert := notificationv1.Alert{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: sourceLabels, Labels: sourceLabels,
}, },
Spec: notificationv1.AlertSpec{ Spec: notificationv1.AlertSpec{
@@ -119,7 +122,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -73,6 +73,9 @@ func init() {
} }
func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error { func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Provider name is required")
}
name := args[0] name := args[0]
if alertProviderArgs.alertType == "" { if alertProviderArgs.alertType == "" {
@@ -91,7 +94,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
provider := notificationv1.Provider{ provider := notificationv1.Provider{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: sourceLabels, Labels: sourceLabels,
}, },
Spec: notificationv1.ProviderSpec{ Spec: notificationv1.ProviderSpec{
@@ -115,7 +118,7 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -139,6 +139,9 @@ func init() {
} }
func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error { func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("HelmRelease name is required")
}
name := args[0] name := args[0]
if helmReleaseArgs.chart == "" { if helmReleaseArgs.chart == "" {
@@ -157,7 +160,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
helmRelease := helmv2.HelmRelease{ helmRelease := helmv2.HelmRelease{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: sourceLabels, Labels: sourceLabels,
}, },
Spec: helmv2.HelmReleaseSpec{ Spec: helmv2.HelmReleaseSpec{
@@ -247,7 +250,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -84,6 +84,9 @@ func (obj imagePolicyAdapter) getObservedGeneration() int64 {
} }
func createImagePolicyRun(cmd *cobra.Command, args []string) error { func createImagePolicyRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("ImagePolicy name is required")
}
objectName := args[0] objectName := args[0]
if imagePolicyArgs.imageRef == "" { if imagePolicyArgs.imageRef == "" {
@@ -98,7 +101,7 @@ func createImagePolicyRun(cmd *cobra.Command, args []string) error {
var policy = imagev1.ImagePolicy{ var policy = imagev1.ImagePolicy{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: objectName, Name: objectName,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: labels, Labels: labels,
}, },
Spec: imagev1.ImagePolicySpec{ Spec: imagev1.ImagePolicySpec{

View File

@@ -83,6 +83,9 @@ func init() {
} }
func createImageRepositoryRun(cmd *cobra.Command, args []string) error { func createImageRepositoryRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("ImageRepository name is required")
}
objectName := args[0] objectName := args[0]
if imageRepoArgs.image == "" { if imageRepoArgs.image == "" {
@@ -101,7 +104,7 @@ func createImageRepositoryRun(cmd *cobra.Command, args []string) error {
var repo = imagev1.ImageRepository{ var repo = imagev1.ImageRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: objectName, Name: objectName,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: labels, Labels: labels,
}, },
Spec: imagev1.ImageRepositorySpec{ Spec: imagev1.ImageRepositorySpec{

View File

@@ -23,7 +23,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1" autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var createImageUpdateCmd = &cobra.Command{ var createImageUpdateCmd = &cobra.Command{
@@ -49,40 +49,25 @@ mentioned in YAMLs in a git repository.`,
--push-branch=image-updates \ --push-branch=image-updates \
--author-name=flux \ --author-name=flux \
--author-email=flux@example.com \ --author-email=flux@example.com \
--commit-template="{{range .Updated.Images}}{{println .}}{{end}}" --commit-template="{{range .Updated.Images}}{{println .}}{{end}}"`,
# Configure image updates for a Git repository in a different namespace
flux create image update apps \
--namespace=apps \
--git-repo-ref=flux-system \
--git-repo-namespace=flux-system \
--git-repo-path="./clusters/my-cluster" \
--checkout-branch=main \
--push-branch=image-updates \
--author-name=flux \
--author-email=flux@example.com \
--commit-template="{{range .Updated.Images}}{{println .}}{{end}}"
`,
RunE: createImageUpdateRun, RunE: createImageUpdateRun,
} }
type imageUpdateFlags struct { type imageUpdateFlags struct {
gitRepoName string gitRepoRef string
gitRepoNamespace string gitRepoPath string
gitRepoPath string checkoutBranch string
checkoutBranch string pushBranch string
pushBranch string commitTemplate string
commitTemplate string authorName string
authorName string authorEmail string
authorEmail string
} }
var imageUpdateArgs = imageUpdateFlags{} var imageUpdateArgs = imageUpdateFlags{}
func init() { func init() {
flags := createImageUpdateCmd.Flags() flags := createImageUpdateCmd.Flags()
flags.StringVar(&imageUpdateArgs.gitRepoName, "git-repo-ref", "", "the name of a GitRepository resource with details of the upstream Git repository") flags.StringVar(&imageUpdateArgs.gitRepoRef, "git-repo-ref", "", "the name of a GitRepository resource with details of the upstream Git repository")
flags.StringVar(&imageUpdateArgs.gitRepoNamespace, "git-repo-namespace", "", "the namespace of the GitRepository resource, defaults to the ImageUpdateAutomation namespace")
flags.StringVar(&imageUpdateArgs.gitRepoPath, "git-repo-path", "", "path to the directory containing the manifests to be updated, defaults to the repository root") flags.StringVar(&imageUpdateArgs.gitRepoPath, "git-repo-path", "", "path to the directory containing the manifests to be updated, defaults to the repository root")
flags.StringVar(&imageUpdateArgs.checkoutBranch, "checkout-branch", "", "the branch to checkout") flags.StringVar(&imageUpdateArgs.checkoutBranch, "checkout-branch", "", "the branch to checkout")
flags.StringVar(&imageUpdateArgs.pushBranch, "push-branch", "", "the branch to push commits to, defaults to the checkout branch if not specified") flags.StringVar(&imageUpdateArgs.pushBranch, "push-branch", "", "the branch to push commits to, defaults to the checkout branch if not specified")
@@ -94,9 +79,12 @@ func init() {
} }
func createImageUpdateRun(cmd *cobra.Command, args []string) error { func createImageUpdateRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("ImageUpdateAutomation name is required")
}
objectName := args[0] objectName := args[0]
if imageUpdateArgs.gitRepoName == "" { if imageUpdateArgs.gitRepoRef == "" {
return fmt.Errorf("a reference to a GitRepository is required (--git-repo-ref)") return fmt.Errorf("a reference to a GitRepository is required (--git-repo-ref)")
} }
@@ -120,14 +108,13 @@ func createImageUpdateRun(cmd *cobra.Command, args []string) error {
var update = autov1.ImageUpdateAutomation{ var update = autov1.ImageUpdateAutomation{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: objectName, Name: objectName,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: labels, Labels: labels,
}, },
Spec: autov1.ImageUpdateAutomationSpec{ Spec: autov1.ImageUpdateAutomationSpec{
SourceRef: autov1.CrossNamespaceSourceReference{ SourceRef: autov1.SourceReference{
Kind: sourcev1.GitRepositoryKind, Kind: sourcev1.GitRepositoryKind,
Name: imageUpdateArgs.gitRepoName, Name: imageUpdateArgs.gitRepoRef,
Namespace: imageUpdateArgs.gitRepoNamespace,
}, },
GitSpec: &autov1.GitSpec{ GitSpec: &autov1.GitSpec{

View File

@@ -119,6 +119,9 @@ func NewKustomizationFlags() kustomizationFlags {
} }
func createKsCmdRun(cmd *cobra.Command, args []string) error { func createKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Kustomization name is required")
}
name := args[0] name := args[0]
if kustomizationArgs.path == "" { if kustomizationArgs.path == "" {
@@ -140,7 +143,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
kustomization := kustomizev1.Kustomization{ kustomization := kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: kslabels, Labels: kslabels,
}, },
Spec: kustomizev1.KustomizationSpec{ Spec: kustomizev1.KustomizationSpec{
@@ -229,7 +232,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -67,6 +67,9 @@ func init() {
} }
func createReceiverCmdRun(cmd *cobra.Command, args []string) error { func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Receiver name is required")
}
name := args[0] name := args[0]
if receiverArgs.receiverType == "" { if receiverArgs.receiverType == "" {
@@ -106,7 +109,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
receiver := notificationv1.Receiver{ receiver := notificationv1.Receiver{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: sourceLabels, Labels: sourceLabels,
}, },
Spec: notificationv1.ReceiverSpec{ Spec: notificationv1.ReceiverSpec{
@@ -127,7 +130,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -112,6 +112,9 @@ func NewSecretGitFlags() secretGitFlags {
} }
func createSecretGitCmdRun(cmd *cobra.Command, args []string) error { func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("secret name is required")
}
name := args[0] name := args[0]
if secretGitArgs.url == "" { if secretGitArgs.url == "" {
return fmt.Errorf("url is required") return fmt.Errorf("url is required")
@@ -129,7 +132,7 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
opts := sourcesecret.Options{ opts := sourcesecret.Options{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: labels, Labels: labels,
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile, ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
@@ -173,14 +176,14 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
if err := upsertSecret(ctx, kubeClient, s); err != nil { if err := upsertSecret(ctx, kubeClient, s); err != nil {
return err return err
} }
logger.Actionf("git secret '%s' created in '%s' namespace", name, *kubeconfigArgs.Namespace) logger.Actionf("git secret '%s' created in '%s' namespace", name, rootArgs.namespace)
return nil return nil
} }

View File

@@ -13,7 +13,7 @@ func TestCreateGitSecret(t *testing.T) {
{ {
name: "no args", name: "no args",
args: "create secret git", args: "create secret git",
assert: assertError("name is required"), assert: assertError("secret name is required"),
}, },
{ {
name: "basic secret", name: "basic secret",

View File

@@ -18,6 +18,7 @@ package main
import ( import (
"context" "context"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@@ -67,6 +68,9 @@ func init() {
} }
func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error { func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("secret name is required")
}
name := args[0] name := args[0]
labels, err := parseLabels() labels, err := parseLabels()
@@ -76,7 +80,7 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
opts := sourcesecret.Options{ opts := sourcesecret.Options{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: labels, Labels: labels,
Username: secretHelmArgs.username, Username: secretHelmArgs.username,
Password: secretHelmArgs.password, Password: secretHelmArgs.password,
@@ -96,7 +100,7 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
@@ -108,6 +112,6 @@ func createSecretHelmCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Actionf("helm secret '%s' created in '%s' namespace", name, *kubeconfigArgs.Namespace) logger.Actionf("helm secret '%s' created in '%s' namespace", name, rootArgs.namespace)
return nil return nil
} }

View File

@@ -12,7 +12,7 @@ func TestCreateHelmSecret(t *testing.T) {
}{ }{
{ {
args: "create secret helm", args: "create secret helm",
assert: assertError("name is required"), assert: assertError("secret name is required"),
}, },
{ {
args: "create secret helm helm-secret --username=my-username --password=my-password --namespace=my-namespace --export", args: "create secret helm helm-secret --username=my-username --password=my-password --namespace=my-namespace --export",

View File

@@ -18,6 +18,7 @@ package main
import ( import (
"context" "context"
"fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@@ -66,6 +67,9 @@ func init() {
} }
func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error { func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("secret name is required")
}
name := args[0] name := args[0]
labels, err := parseLabels() labels, err := parseLabels()
@@ -75,7 +79,7 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
opts := sourcesecret.Options{ opts := sourcesecret.Options{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: labels, Labels: labels,
CAFilePath: secretTLSArgs.caFile, CAFilePath: secretTLSArgs.caFile,
CertFilePath: secretTLSArgs.certFile, CertFilePath: secretTLSArgs.certFile,
@@ -93,7 +97,7 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
@@ -105,6 +109,6 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Actionf("tls secret '%s' created in '%s' namespace", name, *kubeconfigArgs.Namespace) logger.Actionf("tls secret '%s' created in '%s' namespace", name, rootArgs.namespace)
return nil return nil
} }

View File

@@ -12,7 +12,7 @@ func TestCreateTlsSecretNoArgs(t *testing.T) {
}{ }{
{ {
args: "create secret tls", args: "create secret tls",
assert: assertError("name is required"), assert: assertError("secret name is required"),
}, },
{ {
args: "create secret tls certs --namespace=my-namespace --cert-file=./testdata/create_secret/tls/test-cert.pem --key-file=./testdata/create_secret/tls/test-key.pem --export", args: "create secret tls certs --namespace=my-namespace --cert-file=./testdata/create_secret/tls/test-cert.pem --key-file=./testdata/create_secret/tls/test-key.pem --export",

View File

@@ -30,9 +30,7 @@ import (
"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"
"github.com/fluxcd/pkg/runtime/conditions" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
@@ -95,6 +93,9 @@ func NewSourceBucketFlags() sourceBucketFlags {
} }
func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error { func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Bucket source name is required")
}
name := args[0] name := args[0]
if sourceBucketArgs.name == "" { if sourceBucketArgs.name == "" {
@@ -119,7 +120,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
bucket := &sourcev1.Bucket{ bucket := &sourcev1.Bucket{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: sourceLabels, Labels: sourceLabels,
}, },
Spec: sourcev1.BucketSpec{ Spec: sourcev1.BucketSpec{
@@ -151,7 +152,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
@@ -164,7 +165,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
secret := corev1.Secret{ secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: secretName, Name: secretName,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: sourceLabels, Labels: sourceLabels,
}, },
StringData: map[string]string{}, StringData: map[string]string{},
@@ -237,30 +238,3 @@ func upsertBucket(ctx context.Context, kubeClient client.Client,
logger.Successf("Bucket source updated") logger.Successf("Bucket source updated")
return namespacedName, nil return namespacedName, nil
} }
func isBucketReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, bucket)
if err != nil {
return false, err
}
if c := conditions.Get(bucket, meta.ReadyCondition); c != nil {
// Confirm the Ready condition we are observing is for the
// current generation
if c.ObservedGeneration != bucket.GetGeneration() {
return false, nil
}
// Further check the Status
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}

View File

@@ -23,21 +23,19 @@ import (
"net/url" "net/url"
"os" "os"
"github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
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/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/fluxcd/flux2/internal/flags" "github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret" "github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
@@ -152,6 +150,9 @@ func newSourceGitFlags() sourceGitFlags {
} }
func createSourceGitCmdRun(cmd *cobra.Command, args []string) error { func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("GitRepository source name is required")
}
name := args[0] name := args[0]
if sourceGitArgs.url == "" { if sourceGitArgs.url == "" {
@@ -171,7 +172,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
} }
if sourceGitArgs.caFile != "" && u.Scheme == "ssh" { if sourceGitArgs.caFile != "" && u.Scheme == "ssh" {
return fmt.Errorf("specifying a CA file is not supported for Git over SSH") return fmt.Errorf("specifing a CA file is not supported for Git over SSH")
} }
if sourceGitArgs.recurseSubmodules && sourceGitArgs.gitImplementation == sourcev1.LibGit2Implementation { if sourceGitArgs.recurseSubmodules && sourceGitArgs.gitImplementation == sourcev1.LibGit2Implementation {
@@ -192,7 +193,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
gitRepository := sourcev1.GitRepository{ gitRepository := sourcev1.GitRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: sourceLabels, Labels: sourceLabels,
}, },
Spec: sourcev1.GitRepositorySpec{ Spec: sourcev1.GitRepositorySpec{
@@ -234,7 +235,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
@@ -243,7 +244,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if sourceGitArgs.secretRef == "" { if sourceGitArgs.secretRef == "" {
secretOpts := sourcesecret.Options{ secretOpts := sourcesecret.Options{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile, ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
} }
switch u.Scheme { switch u.Scheme {
@@ -357,14 +358,7 @@ func isGitRepositoryReady(ctx context.Context, kubeClient client.Client,
return false, err return false, err
} }
if c := conditions.Get(gitRepository, meta.ReadyCondition); c != nil { if c := apimeta.FindStatusCondition(gitRepository.Status.Conditions, meta.ReadyCondition); c != nil {
// Confirm the Ready condition we are observing is for the
// current generation
if c.ObservedGeneration != gitRepository.GetGeneration() {
return false, nil
}
// Further check the Status
switch c.Status { switch c.Status {
case metav1.ConditionTrue: case metav1.ConditionTrue:
return true, nil return true, nil

View File

@@ -1,4 +1,3 @@
//go:build unit
// +build unit // +build unit
/* /*
@@ -21,17 +20,15 @@ package main
import ( import (
"context" "context"
"testing"
"time"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
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/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"testing"
"time"
) )
var pollInterval = 50 * time.Millisecond var pollInterval = 50 * time.Millisecond
@@ -98,21 +95,14 @@ func TestCreateSourceGit(t *testing.T) {
{ {
"NoArgs", "NoArgs",
"create source git", "create source git",
assertError("name is required"), assertError("GitRepository source name is required"),
nil, nil,
}, { }, {
"Succeeded", "Succeeded",
command, command,
assertGoldenFile("testdata/create_source_git/success.golden"), assertGoldenFile("testdata/create_source_git/success.golden"),
func(repo *sourcev1.GitRepository) { func(repo *sourcev1.GitRepository) {
newCondition := metav1.Condition{ meta.SetResourceCondition(repo, meta.ReadyCondition, metav1.ConditionTrue, sourcev1.GitOperationSucceedReason, "succeeded message")
Type: meta.ReadyCondition,
Status: metav1.ConditionTrue,
Reason: sourcev1.GitOperationSucceedReason,
Message: "succeeded message",
ObservedGeneration: repo.GetGeneration(),
}
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
repo.Status.Artifact = &sourcev1.Artifact{ repo.Status.Artifact = &sourcev1.Artifact{
Path: "some-path", Path: "some-path",
Revision: "v1", Revision: "v1",
@@ -123,14 +113,7 @@ func TestCreateSourceGit(t *testing.T) {
command, command,
assertError("failed message"), assertError("failed message"),
func(repo *sourcev1.GitRepository) { func(repo *sourcev1.GitRepository) {
newCondition := metav1.Condition{ meta.SetResourceCondition(repo, meta.ReadyCondition, metav1.ConditionFalse, sourcev1.URLInvalidReason, "failed message")
Type: meta.ReadyCondition,
Status: metav1.ConditionFalse,
Reason: sourcev1.URLInvalidReason,
Message: "failed message",
ObservedGeneration: repo.GetGeneration(),
}
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
}, },
}, { }, {
"NoArtifact", "NoArtifact",
@@ -138,14 +121,7 @@ func TestCreateSourceGit(t *testing.T) {
assertError("GitRepository source reconciliation completed but no artifact was found"), assertError("GitRepository source reconciliation completed but no artifact was found"),
func(repo *sourcev1.GitRepository) { func(repo *sourcev1.GitRepository) {
// Updated with no artifact // Updated with no artifact
newCondition := metav1.Condition{ meta.SetResourceCondition(repo, meta.ReadyCondition, metav1.ConditionTrue, sourcev1.GitOperationSucceedReason, "succeeded message")
Type: meta.ReadyCondition,
Status: metav1.ConditionTrue,
Reason: sourcev1.GitOperationSucceedReason,
Message: "succeeded message",
ObservedGeneration: repo.GetGeneration(),
}
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
}, },
}, },
} }

View File

@@ -23,17 +23,17 @@ import (
"os" "os"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
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/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret" "github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
@@ -91,6 +91,9 @@ func init() {
} }
func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error { func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("HelmRepository source name is required")
}
name := args[0] name := args[0]
if sourceHelmArgs.url == "" { if sourceHelmArgs.url == "" {
@@ -115,7 +118,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
helmRepository := &sourcev1.HelmRepository{ helmRepository := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Labels: sourceLabels, Labels: sourceLabels,
}, },
Spec: sourcev1.HelmRepositorySpec{ Spec: sourcev1.HelmRepositorySpec{
@@ -144,7 +147,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
@@ -154,7 +157,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
secretName := fmt.Sprintf("helm-%s", name) secretName := fmt.Sprintf("helm-%s", name)
secretOpts := sourcesecret.Options{ secretOpts := sourcesecret.Options{
Name: secretName, Name: secretName,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Username: sourceHelmArgs.username, Username: sourceHelmArgs.username,
Password: sourceHelmArgs.password, Password: sourceHelmArgs.password,
CertFilePath: sourceHelmArgs.certFile, CertFilePath: sourceHelmArgs.certFile,
@@ -242,14 +245,12 @@ func isHelmRepositoryReady(ctx context.Context, kubeClient client.Client,
return false, err return false, err
} }
if c := conditions.Get(helmRepository, meta.ReadyCondition); c != nil { // Confirm the state we are observing is for the current generation
// Confirm the Ready condition we are observing is for the if helmRepository.Generation != helmRepository.Status.ObservedGeneration {
// current generation return false, nil
if c.ObservedGeneration != helmRepository.GetGeneration() { }
return false, nil
}
// Further check the Status if c := apimeta.FindStatusCondition(helmRepository.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case metav1.ConditionTrue: case metav1.ConditionTrue:
return true, nil return true, nil

View File

@@ -70,6 +70,9 @@ func init() {
} }
func createTenantCmdRun(cmd *cobra.Command, args []string) error { func createTenantCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("tenant name is required")
}
tenant := args[0] tenant := args[0]
if err := validation.IsQualifiedName(tenant); len(err) > 0 { if err := validation.IsQualifiedName(tenant); len(err) > 0 {
return fmt.Errorf("invalid tenant name '%s': %v", tenant, err) return fmt.Errorf("invalid tenant name '%s': %v", tenant, err)
@@ -156,7 +159,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,55 +0,0 @@
package main
import (
"testing"
"k8s.io/apimachinery/pkg/util/rand"
)
func Test_validateObjectName(t *testing.T) {
tests := []struct {
name string
valid bool
}{
{
name: "flux-system",
valid: true,
},
{
name: "-flux-system",
valid: false,
},
{
name: "-flux-system-",
valid: false,
},
{
name: "third.first",
valid: false,
},
{
name: "THirdfirst",
valid: false,
},
{
name: "THirdfirst",
valid: false,
},
{
name: rand.String(63),
valid: true,
},
{
name: rand.String(64),
valid: false,
},
}
for _, tt := range tests {
valid := validateObjectName(tt.name)
if valid != tt.valid {
t.Errorf("expected name %q to return %t for validateObjectName func but got %t",
tt.name, tt.valid, valid)
}
}
}

View File

@@ -60,13 +60,13 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Name: name, Name: name,
} }
@@ -85,7 +85,7 @@ func (del deleteCommand) run(cmd *cobra.Command, args []string) error {
} }
} }
logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, *kubeconfigArgs.Namespace) logger.Actionf("deleting %s %s in %s namespace", del.humanKind, name, rootArgs.namespace)
err = kubeClient.Delete(ctx, del.object.asClientObject()) err = kubeClient.Delete(ctx, del.object.asClientObject())
if err != nil { if err != nil {
return err return err

View File

@@ -19,7 +19,7 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var deleteSourceBucketCmd = &cobra.Command{ var deleteSourceBucketCmd = &cobra.Command{

View File

@@ -19,7 +19,7 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var deleteSourceGitCmd = &cobra.Command{ var deleteSourceGitCmd = &cobra.Command{

View File

@@ -19,7 +19,7 @@ package main
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var deleteSourceHelmCmd = &cobra.Command{ var deleteSourceHelmCmd = &cobra.Command{

View File

@@ -1,31 +0,0 @@
/*
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 (
"github.com/spf13/cobra"
)
var diffCmd = &cobra.Command{
Use: "diff",
Short: "Diff a flux resource",
Long: "The diff command is used to do a server-side dry-run on flux resources, then prints the diff.",
}
func init() {
rootCmd.AddCommand(diffCmd)
}

View File

@@ -1,113 +0,0 @@
/*
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 (
"fmt"
"os"
"os/signal"
"github.com/spf13/cobra"
"github.com/fluxcd/flux2/internal/build"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
)
var diffKsCmd = &cobra.Command{
Use: "kustomization",
Aliases: []string{"ks"},
Short: "Diff Kustomization",
Long: `The diff command does a build, then it performs a server-side dry-run and prints the diff.
Exit status: 0 No differences were found. 1 Differences were found. >1 diff failed with an error.`,
Example: `# Preview local changes as they were applied on the cluster
flux diff kustomization my-app --path ./path/to/local/manifests`,
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
RunE: diffKsCmdRun,
}
type diffKsFlags struct {
path string
progressBar bool
}
var diffKsArgs diffKsFlags
func init() {
diffKsCmd.Flags().StringVar(&diffKsArgs.path, "path", "", "Path to a local directory that matches the specified Kustomization.spec.path.)")
diffKsCmd.Flags().BoolVar(&diffKsArgs.progressBar, "progress-bar", true, "Boolean to set the progress bar. The default value is true.")
diffCmd.AddCommand(diffKsCmd)
}
func diffKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("%s name is required", kustomizationType.humanKind)
}
name := args[0]
if diffKsArgs.path == "" {
return &RequestError{StatusCode: 2, Err: fmt.Errorf("invalid resource path %q", diffKsArgs.path)}
}
if fs, err := os.Stat(diffKsArgs.path); err != nil || !fs.IsDir() {
return &RequestError{StatusCode: 2, Err: fmt.Errorf("invalid resource path %q", diffKsArgs.path)}
}
var builder *build.Builder
var err error
if diffKsArgs.progressBar {
builder, err = build.NewBuilder(kubeconfigArgs, name, diffKsArgs.path, build.WithTimeout(rootArgs.timeout), build.WithProgressBar())
} else {
builder, err = build.NewBuilder(kubeconfigArgs, name, diffKsArgs.path, build.WithTimeout(rootArgs.timeout))
}
if err != nil {
return &RequestError{StatusCode: 2, Err: err}
}
// create a signal channel
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt)
errChan := make(chan error)
go func() {
output, hasChanged, err := builder.Diff()
if err != nil {
errChan <- &RequestError{StatusCode: 2, Err: err}
}
cmd.Print(output)
if hasChanged {
errChan <- &RequestError{StatusCode: 1, Err: fmt.Errorf("identified at least one change, exiting with non-zero exit code")}
} else {
errChan <- nil
}
}()
select {
case <-sigc:
fmt.Println("Build cancelled... exiting.")
return builder.Cancel()
case err := <-errChan:
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,145 +0,0 @@
//go:build unit
// +build unit
/*
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 (
"context"
"os"
"strings"
"testing"
"github.com/fluxcd/flux2/internal/build"
"github.com/fluxcd/pkg/ssa"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func TestDiffKustomization(t *testing.T) {
tests := []struct {
name string
args string
objectFile string
assert assertFunc
}{
{
name: "no args",
args: "diff kustomization podinfo",
objectFile: "",
assert: assertError("invalid resource path \"\""),
},
{
name: "diff nothing deployed",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false",
objectFile: "",
assert: assertGoldenFile("./testdata/diff-kustomization/nothing-is-deployed.golden"),
},
{
name: "diff with a deployment object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false",
objectFile: "./testdata/diff-kustomization/deployment.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-deployment.golden"),
},
{
name: "diff with a drifted service object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false",
objectFile: "./testdata/diff-kustomization/service.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-service.golden"),
},
{
name: "diff with a drifted secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false",
objectFile: "./testdata/diff-kustomization/secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-secret.golden"),
},
{
name: "diff with a drifted key in sops secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false",
objectFile: "./testdata/diff-kustomization/key-sops-secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-key-sops-secret.golden"),
},
{
name: "diff with a drifted value in sops secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false",
objectFile: "./testdata/diff-kustomization/value-sops-secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-value-sops-secret.golden"),
},
{
name: "diff with a sops dockerconfigjson secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false",
objectFile: "./testdata/diff-kustomization/dockerconfigjson-sops-secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-dockerconfigjson-sops-secret.golden"),
},
{
name: "diff with a sops stringdata secret object",
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false",
objectFile: "./testdata/diff-kustomization/stringdata-sops-secret.yaml",
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-stringdata-sops-secret.golden"),
},
}
tmpl := map[string]string{
"fluxns": allocateNamespace("flux-system"),
}
b, _ := build.NewBuilder(kubeconfigArgs, "podinfo", "")
resourceManager, err := b.Manager()
if err != nil {
t.Fatal(err)
}
setup(t, tmpl)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.objectFile != "" {
resourceManager.ApplyAll(context.Background(), createObjectFromFile(tt.objectFile, tmpl, t), ssa.DefaultApplyOptions())
}
cmd := cmdTestCase{
args: tt.args + " -n " + tmpl["fluxns"],
assert: tt.assert,
}
cmd.runTestCmd(t)
if tt.objectFile != "" {
testEnv.DeleteObjectFile(tt.objectFile, tmpl, t)
}
})
}
}
func createObjectFromFile(objectFile string, templateValues map[string]string, t *testing.T) []*unstructured.Unstructured {
buf, err := os.ReadFile(objectFile)
if err != nil {
t.Fatalf("Error reading file '%s': %v", objectFile, err)
}
content, err := executeTemplate(string(buf), templateValues)
if err != nil {
t.Fatalf("Error evaluating template file '%s': '%v'", objectFile, err)
}
clientObjects, err := readYamlObjects(strings.NewReader(content))
if err != nil {
t.Fatalf("Error decoding yaml file '%s': %v", objectFile, err)
}
if err := ssa.SetNativeKindsDefaults(clientObjects); err != nil {
t.Fatalf("Error setting native kinds defaults for '%s': %v", objectFile, err)
}
return clientObjects
}

View File

@@ -20,7 +20,6 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
@@ -74,19 +73,19 @@ func (export exportCommand) run(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
if exportArgs.all { if exportArgs.all {
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(*kubeconfigArgs.Namespace)) err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(rootArgs.namespace))
if err != nil { if err != nil {
return err return err
} }
if export.list.len() == 0 { if export.list.len() == 0 {
return fmt.Errorf("no objects found in %s namespace", *kubeconfigArgs.Namespace) return fmt.Errorf("no objects found in %s namespace", rootArgs.namespace)
} }
for i := 0; i < export.list.len(); i++ { for i := 0; i < export.list.len(); i++ {
@@ -97,7 +96,7 @@ func (export exportCommand) run(cmd *cobra.Command, args []string) error {
} else { } else {
name := args[0] name := args[0]
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Name: name, Name: name,
} }
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject()) err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())

View File

@@ -19,7 +19,6 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -59,19 +58,19 @@ func (export exportWithSecretCommand) run(cmd *cobra.Command, args []string) err
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
if exportArgs.all { if exportArgs.all {
err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(*kubeconfigArgs.Namespace)) err = kubeClient.List(ctx, export.list.asClientList(), client.InNamespace(rootArgs.namespace))
if err != nil { if err != nil {
return err return err
} }
if export.list.len() == 0 { if export.list.len() == 0 {
return fmt.Errorf("no objects found in %s namespace", *kubeconfigArgs.Namespace) return fmt.Errorf("no objects found in %s namespace", rootArgs.namespace)
} }
for i := 0; i < export.list.len(); i++ { for i := 0; i < export.list.len(); i++ {
@@ -89,7 +88,7 @@ func (export exportWithSecretCommand) run(cmd *cobra.Command, args []string) err
} else { } else {
name := args[0] name := args[0]
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Name: name, Name: name,
} }
err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject()) err = kubeClient.Get(ctx, namespacedName, export.object.asClientObject())

View File

@@ -21,7 +21,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var exportSourceBucketCmd = &cobra.Command{ var exportSourceBucketCmd = &cobra.Command{

View File

@@ -21,7 +21,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var exportSourceGitCmd = &cobra.Command{ var exportSourceGitCmd = &cobra.Command{

View File

@@ -21,7 +21,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var exportSourceHelmCmd = &cobra.Command{ var exportSourceHelmCmd = &cobra.Command{

View File

@@ -1,4 +1,3 @@
//go:build unit
// +build unit // +build unit
package main package main

View File

@@ -33,7 +33,6 @@ import (
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/flux2/internal/utils" "github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/printers"
) )
type deriveType func(runtime.Object) (summarisable, error) type deriveType func(runtime.Object) (summarisable, error)
@@ -136,14 +135,14 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
var listOpts []client.ListOption var listOpts []client.ListOption
if !getArgs.allNamespaces { if !getArgs.allNamespaces {
listOpts = append(listOpts, client.InNamespace(*kubeconfigArgs.Namespace)) listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
} }
if len(args) > 0 { if len(args) > 0 {
@@ -163,7 +162,7 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
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, *kubeconfigArgs.Namespace) logger.Failuref("no %s objects found in %s namespace", get.kind, rootArgs.namespace)
} }
return nil return nil
} }
@@ -178,10 +177,7 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
return err return err
} }
err = printers.TablePrinter(header).Print(cmd.OutOrStdout(), rows) utils.PrintTable(cmd.OutOrStdout(), header, rows)
if err != nil {
return err
}
if getAll { if getAll {
fmt.Println() fmt.Println()
@@ -246,16 +242,10 @@ func watchUntil(ctx context.Context, w watch.Interface, get *getCommand) (bool,
return false, err return false, err
} }
if firstIteration { if firstIteration {
err = printers.TablePrinter(header).Print(os.Stdout, rows) utils.PrintTable(os.Stdout, header, rows)
if err != nil {
return false, err
}
firstIteration = false firstIteration = false
} else { } else {
err = printers.TablePrinter([]string{}).Print(os.Stdout, rows) utils.PrintTable(os.Stdout, []string{}, rows)
if err != nil {
return false, err
}
} }
return false, nil return false, nil

View File

@@ -77,11 +77,11 @@ func init() {
func (s alertListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string { func (s alertListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i] item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), strings.Title(strconv.FormatBool(item.Spec.Suspend)), status, msg) return append(nameColumns(&item, includeNamespace, includeKind), status, msg, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (s alertListAdapter) headers(includeNamespace bool) []string { func (s alertListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Suspended", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Suspended"}
if includeNamespace { if includeNamespace {
return append(namespaceHeader, headers...) return append(namespaceHeader, headers...)
} }

View File

@@ -75,11 +75,11 @@ func (a helmReleaseListAdapter) summariseItem(i int, includeNamespace bool, incl
revision := item.Status.LastAppliedRevision revision := item.Status.LastAppliedRevision
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), return append(nameColumns(&item, includeNamespace, includeKind),
revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)), status, msg) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (a helmReleaseListAdapter) headers(includeNamespace bool) []string { func (a helmReleaseListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Revision", "Suspended", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace { if includeNamespace {
headers = append([]string{"Namespace"}, headers...) headers = append([]string{"Namespace"}, headers...)
} }

View File

@@ -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() {

View File

@@ -74,11 +74,11 @@ func init() {
func (s imagePolicyListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string { func (s imagePolicyListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i] item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), item.Status.LatestImage, status, msg) return append(nameColumns(&item, includeNamespace, includeKind), status, msg, item.Status.LatestImage)
} }
func (s imagePolicyListAdapter) headers(includeNamespace bool) []string { func (s imagePolicyListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Latest image", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Latest image"}
if includeNamespace { if includeNamespace {
return append(namespaceHeader, headers...) return append(namespaceHeader, headers...)
} }

View File

@@ -82,11 +82,11 @@ func (s imageRepositoryListAdapter) summariseItem(i int, includeNamespace bool,
lastScan = item.Status.LastScanResult.ScanTime.Time.Format(time.RFC3339) lastScan = item.Status.LastScanResult.ScanTime.Time.Format(time.RFC3339)
} }
return append(nameColumns(&item, includeNamespace, includeKind), return append(nameColumns(&item, includeNamespace, includeKind),
lastScan, strings.Title(strconv.FormatBool(item.Spec.Suspend)), status, msg) status, msg, lastScan, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (s imageRepositoryListAdapter) headers(includeNamespace bool) []string { func (s imageRepositoryListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Last scan", "Suspended", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Last scan", "Suspended"}
if includeNamespace { if includeNamespace {
return append(namespaceHeader, headers...) return append(namespaceHeader, headers...)
} }

View File

@@ -81,11 +81,11 @@ func (s imageUpdateAutomationListAdapter) summariseItem(i int, includeNamespace
if item.Status.LastAutomationRunTime != nil { if item.Status.LastAutomationRunTime != nil {
lastRun = item.Status.LastAutomationRunTime.Time.Format(time.RFC3339) lastRun = item.Status.LastAutomationRunTime.Time.Format(time.RFC3339)
} }
return append(nameColumns(&item, includeNamespace, includeKind), lastRun, strings.Title(strconv.FormatBool(item.Spec.Suspend)), status, msg) return append(nameColumns(&item, includeNamespace, includeKind), status, msg, lastRun, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (s imageUpdateAutomationListAdapter) headers(includeNamespace bool) []string { func (s imageUpdateAutomationListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Last run", "Suspended", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Last run", "Suspended"}
if includeNamespace { if includeNamespace {
return append(namespaceHeader, headers...) return append(namespaceHeader, headers...)
} }

View File

@@ -18,12 +18,10 @@ package main
import ( import (
"fmt" "fmt"
"regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
@@ -80,16 +78,12 @@ func (a kustomizationListAdapter) summariseItem(i int, includeNamespace bool, in
item := a.Items[i] item := a.Items[i]
revision := item.Status.LastAppliedRevision revision := item.Status.LastAppliedRevision
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
if status == string(metav1.ConditionTrue) {
revision = shortenCommitSha(revision)
msg = shortenCommitSha(msg)
}
return append(nameColumns(&item, includeNamespace, includeKind), return append(nameColumns(&item, includeNamespace, includeKind),
revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)), status, msg) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (a kustomizationListAdapter) headers(includeNamespace bool) []string { func (a kustomizationListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Revision", "Suspended", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace { if includeNamespace {
headers = append([]string{"Namespace"}, headers...) headers = append([]string{"Namespace"}, headers...)
} }
@@ -100,13 +94,3 @@ func (a kustomizationListAdapter) statusSelectorMatches(i int, conditionType, co
item := a.Items[i] item := a.Items[i]
return statusMatches(conditionType, conditionStatus, item.Status.Conditions) return statusMatches(conditionType, conditionStatus, item.Status.Conditions)
} }
func shortenCommitSha(msg string) string {
r := regexp.MustCompile("/([a-f0-9]{40})$")
sha := r.FindString(msg)
if sha != "" {
msg = strings.Replace(msg, sha, string([]rune(sha)[:8]), -1)
}
return msg
}

View File

@@ -74,11 +74,11 @@ func init() {
func (s receiverListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string { func (s receiverListAdapter) summariseItem(i int, includeNamespace bool, includeKind bool) []string {
item := s.Items[i] item := s.Items[i]
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), strings.Title(strconv.FormatBool(item.Spec.Suspend)), status, msg) return append(nameColumns(&item, includeNamespace, includeKind), status, msg, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (s receiverListAdapter) headers(includeNamespace bool) []string { func (s receiverListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Suspended", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Suspended"}
if includeNamespace { if includeNamespace {
return append(namespaceHeader, headers...) return append(namespaceHeader, headers...)
} }

View File

@@ -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() {

View File

@@ -21,7 +21,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var getSourceAllCmd = &cobra.Command{ var getSourceAllCmd = &cobra.Command{

View File

@@ -24,7 +24,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var getSourceBucketCmd = &cobra.Command{ var getSourceBucketCmd = &cobra.Command{
@@ -81,11 +81,11 @@ func (a *bucketListAdapter) summariseItem(i int, includeNamespace bool, includeK
} }
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), return append(nameColumns(&item, includeNamespace, includeKind),
revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)), status, msg) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (a bucketListAdapter) headers(includeNamespace bool) []string { func (a bucketListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Revision", "Suspended", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace { if includeNamespace {
headers = append([]string{"Namespace"}, headers...) headers = append([]string{"Namespace"}, headers...)
} }

View File

@@ -24,7 +24,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var getSourceHelmChartCmd = &cobra.Command{ var getSourceHelmChartCmd = &cobra.Command{
@@ -81,11 +81,11 @@ func (a *helmChartListAdapter) summariseItem(i int, includeNamespace bool, inclu
} }
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), return append(nameColumns(&item, includeNamespace, includeKind),
revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)), status, msg) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (a helmChartListAdapter) headers(includeNamespace bool) []string { func (a helmChartListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Revision", "Suspended", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace { if includeNamespace {
headers = append([]string{"Namespace"}, headers...) headers = append([]string{"Namespace"}, headers...)
} }

View File

@@ -22,10 +22,9 @@ import (
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var getSourceGitCmd = &cobra.Command{ var getSourceGitCmd = &cobra.Command{
@@ -81,16 +80,12 @@ func (a *gitRepositoryListAdapter) summariseItem(i int, includeNamespace bool, i
revision = item.GetArtifact().Revision revision = item.GetArtifact().Revision
} }
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
if status == string(metav1.ConditionTrue) {
revision = shortenCommitSha(revision)
msg = shortenCommitSha(msg)
}
return append(nameColumns(&item, includeNamespace, includeKind), return append(nameColumns(&item, includeNamespace, includeKind),
revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)), status, msg) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (a gitRepositoryListAdapter) headers(includeNamespace bool) []string { func (a gitRepositoryListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Revision", "Suspended", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace { if includeNamespace {
headers = append([]string{"Namespace"}, headers...) headers = append([]string{"Namespace"}, headers...)
} }

View File

@@ -24,7 +24,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var getSourceHelmCmd = &cobra.Command{ var getSourceHelmCmd = &cobra.Command{
@@ -81,11 +81,11 @@ func (a *helmRepositoryListAdapter) summariseItem(i int, includeNamespace bool,
} }
status, msg := statusAndMessage(item.Status.Conditions) status, msg := statusAndMessage(item.Status.Conditions)
return append(nameColumns(&item, includeNamespace, includeKind), return append(nameColumns(&item, includeNamespace, includeKind),
revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)), status, msg) status, msg, revision, strings.Title(strconv.FormatBool(item.Spec.Suspend)))
} }
func (a helmRepositoryListAdapter) headers(includeNamespace bool) []string { func (a helmRepositoryListAdapter) headers(includeNamespace bool) []string {
headers := []string{"Name", "Revision", "Suspended", "Ready", "Message"} headers := []string{"Name", "Ready", "Message", "Revision", "Suspended"}
if includeNamespace { if includeNamespace {
headers = append([]string{"Namespace"}, headers...) headers = append([]string{"Namespace"}, headers...)
} }

View File

@@ -25,9 +25,8 @@ import (
// helmv2.HelmRelease // helmv2.HelmRelease
var helmReleaseType = apiType{ var helmReleaseType = apiType{
kind: helmv2.HelmReleaseKind, kind: helmv2.HelmReleaseKind,
humanKind: "helmrelease", humanKind: "helmreleases",
groupVersion: helmv2.GroupVersion,
} }
type helmReleaseAdapter struct { type helmReleaseAdapter struct {

View File

@@ -1,4 +1,3 @@
//go:build e2e
// +build e2e // +build e2e
/* /*

View File

@@ -30,9 +30,8 @@ import (
// imagev1.ImageRepository // imagev1.ImageRepository
var imageRepositoryType = apiType{ var imageRepositoryType = apiType{
kind: imagev1.ImageRepositoryKind, kind: imagev1.ImageRepositoryKind,
humanKind: "image repository", humanKind: "image repository",
groupVersion: imagev1.GroupVersion,
} }
type imageRepositoryAdapter struct { type imageRepositoryAdapter struct {
@@ -64,9 +63,8 @@ func (a imageRepositoryListAdapter) len() int {
// imagev1.ImagePolicy // imagev1.ImagePolicy
var imagePolicyType = apiType{ var imagePolicyType = apiType{
kind: imagev1.ImagePolicyKind, kind: imagev1.ImagePolicyKind,
humanKind: "image policy", humanKind: "image policy",
groupVersion: imagev1.GroupVersion,
} }
type imagePolicyAdapter struct { type imagePolicyAdapter struct {
@@ -94,9 +92,8 @@ func (a imagePolicyListAdapter) len() int {
// autov1.ImageUpdateAutomation // autov1.ImageUpdateAutomation
var imageUpdateAutomationType = apiType{ var imageUpdateAutomationType = apiType{
kind: autov1.ImageUpdateAutomationKind, kind: autov1.ImageUpdateAutomationKind,
humanKind: "image update automation", humanKind: "image update automation",
groupVersion: autov1.GroupVersion,
} }
type imageUpdateAutomationAdapter struct { type imageUpdateAutomationAdapter struct {

View File

@@ -1,4 +1,3 @@
//go:build e2e
// +build e2e // +build e2e
package main package main

View File

@@ -37,13 +37,10 @@ var installCmd = &cobra.Command{
Long: `The install command deploys Flux in the specified namespace. Long: `The install command deploys Flux in the specified namespace.
If a previous version is installed, then an in-place upgrade will be performed.`, If a previous version is installed, then an in-place upgrade will be performed.`,
Example: ` # Install the latest version in the flux-system namespace Example: ` # Install the latest version in the flux-system namespace
flux install --namespace=flux-system flux install --version=latest --namespace=flux-system
# Install a specific series of components # Install a specific version and a series of components
flux install --components="source-controller,kustomize-controller" flux install --version=v0.0.7 --components="source-controller,kustomize-controller"
# Install all components including the image automation ones
flux install --components-extra="image-reflector-controller,image-automation-controller"
# Install Flux onto tainted Kubernetes nodes # Install Flux onto tainted Kubernetes nodes
flux install --toleration-keys=node.kubernetes.io/dedicated-to-flux flux install --toleration-keys=node.kubernetes.io/dedicated-to-flux
@@ -87,7 +84,7 @@ func init() {
installCmd.Flags().StringSliceVar(&installArgs.defaultComponents, "components", rootArgs.defaults.Components, installCmd.Flags().StringSliceVar(&installArgs.defaultComponents, "components", rootArgs.defaults.Components,
"list of components, accepts comma-separated values") "list of components, accepts comma-separated values")
installCmd.Flags().StringSliceVar(&installArgs.extraComponents, "components-extra", nil, installCmd.Flags().StringSliceVar(&installArgs.extraComponents, "components-extra", nil,
"list of components in addition to those supplied or defaulted, accepts values such as 'image-reflector-controller,image-automation-controller'") "list of components in addition to those supplied or defaulted, accepts comma-separated values")
installCmd.Flags().StringVar(&installArgs.manifestsPath, "manifests", "", "path to the manifest directory") installCmd.Flags().StringVar(&installArgs.manifestsPath, "manifests", "", "path to the manifest directory")
installCmd.Flags().StringVar(&installArgs.registry, "registry", rootArgs.defaults.Registry, installCmd.Flags().StringVar(&installArgs.registry, "registry", rootArgs.defaults.Registry,
"container registry where the toolkit images are published") "container registry where the toolkit images are published")
@@ -134,7 +131,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
logger.Generatef("generating manifests") logger.Generatef("generating manifests")
} }
tmpDir, err := os.MkdirTemp("", *kubeconfigArgs.Namespace) tmpDir, err := os.MkdirTemp("", rootArgs.namespace)
if err != nil { if err != nil {
return err return err
} }
@@ -151,7 +148,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
opts := install.Options{ opts := install.Options{
BaseURL: installArgs.manifestsPath, BaseURL: installArgs.manifestsPath,
Version: installArgs.version, Version: installArgs.version,
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Components: components, Components: components,
Registry: installArgs.registry, Registry: installArgs.registry,
ImagePullSecret: installArgs.imagePullSecret, ImagePullSecret: installArgs.imagePullSecret,
@@ -159,7 +156,7 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
NetworkPolicy: installArgs.networkPolicy, NetworkPolicy: installArgs.networkPolicy,
LogLevel: installArgs.logLevel.String(), LogLevel: installArgs.logLevel.String(),
NotificationController: rootArgs.defaults.NotificationController, NotificationController: rootArgs.defaults.NotificationController,
ManifestFile: fmt.Sprintf("%s.yaml", *kubeconfigArgs.Namespace), ManifestFile: fmt.Sprintf("%s.yaml", rootArgs.namespace),
Timeout: rootArgs.timeout, Timeout: rootArgs.timeout,
ClusterDomain: installArgs.clusterDomain, ClusterDomain: installArgs.clusterDomain,
TolerationKeys: installArgs.tolerationKeys, TolerationKeys: installArgs.tolerationKeys,
@@ -186,21 +183,21 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
} }
logger.Successf("manifests build completed") logger.Successf("manifests build completed")
logger.Actionf("installing components in %s namespace", *kubeconfigArgs.Namespace) logger.Actionf("installing components in %s namespace", rootArgs.namespace)
if installArgs.dryRun { if installArgs.dryRun {
logger.Successf("install dry-run finished") logger.Successf("install dry-run finished")
return nil return nil
} }
applyOutput, err := utils.Apply(ctx, kubeconfigArgs, filepath.Join(tmpDir, manifest.Path)) applyOutput, err := utils.Apply(ctx, rootArgs.kubeconfig, rootArgs.kubecontext, filepath.Join(tmpDir, manifest.Path))
if err != nil { if err != nil {
return fmt.Errorf("install failed: %w", err) return fmt.Errorf("install failed: %w", err)
} }
fmt.Fprintln(os.Stderr, applyOutput) fmt.Fprintln(os.Stderr, applyOutput)
kubeConfig, err := utils.KubeConfig(kubeconfigArgs) kubeConfig, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return fmt.Errorf("install failed: %w", err) return fmt.Errorf("install failed: %w", err)
} }

View File

@@ -25,9 +25,8 @@ import (
// kustomizev1.Kustomization // kustomizev1.Kustomization
var kustomizationType = apiType{ var kustomizationType = apiType{
kind: kustomizev1.KustomizationKind, kind: kustomizev1.KustomizationKind,
humanKind: "kustomization", humanKind: "kustomizations",
groupVersion: kustomizev1.GroupVersion,
} }
type kustomizationAdapter struct { type kustomizationAdapter struct {

View File

@@ -1,4 +1,3 @@
//go:build e2e
// +build e2e // +build e2e
/* /*

View File

@@ -21,12 +21,12 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"io" "io"
"os" "os"
"sort" "sort"
"strings" "strings"
"sync" "sync"
"text/template"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -99,7 +99,7 @@ func logsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
cfg, err := utils.KubeConfig(kubeconfigArgs) cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
@@ -278,7 +278,7 @@ func filterPrintLog(t *template.Template, l *ControllerLogEntry) {
if logsArgs.logLevel != "" && logsArgs.logLevel != l.Level || if logsArgs.logLevel != "" && logsArgs.logLevel != l.Level ||
logsArgs.kind != "" && strings.ToLower(logsArgs.kind) != strings.ToLower(l.Kind) || logsArgs.kind != "" && strings.ToLower(logsArgs.kind) != strings.ToLower(l.Kind) ||
logsArgs.name != "" && strings.ToLower(logsArgs.name) != strings.ToLower(l.Name) || logsArgs.name != "" && strings.ToLower(logsArgs.name) != strings.ToLower(l.Name) ||
!logsArgs.allNamespaces && strings.ToLower(*kubeconfigArgs.Namespace) != strings.ToLower(l.Namespace) { !logsArgs.allNamespaces && strings.ToLower(rootArgs.namespace) != strings.ToLower(l.Namespace) {
return return
} }

View File

@@ -1,4 +1,3 @@
//go:build unit
// +build unit // +build unit
/* /*

View File

@@ -21,13 +21,13 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"path/filepath"
"strings" "strings"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/term" "golang.org/x/term"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/cli-runtime/pkg/genericclioptions"
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
"github.com/fluxcd/flux2/pkg/manifestgen/install" "github.com/fluxcd/flux2/pkg/manifestgen/install"
@@ -99,43 +99,29 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.`,
var logger = stderrLogger{stderr: os.Stderr} var logger = stderrLogger{stderr: os.Stderr}
type rootFlags struct { type rootFlags struct {
kubeconfig string
kubecontext string
namespace string
timeout time.Duration timeout time.Duration
verbose bool verbose bool
pollInterval time.Duration pollInterval time.Duration
defaults install.Options defaults install.Options
} }
// RequestError is a custom error type that wraps an error returned by the flux api.
type RequestError struct {
StatusCode int
Err error
}
func (r *RequestError) Error() string {
return r.Err.Error()
}
var rootArgs = NewRootFlags() var rootArgs = NewRootFlags()
var kubeconfigArgs = genericclioptions.NewConfigFlags(false)
func init() { func init() {
rootCmd.PersistentFlags().StringVarP(&rootArgs.namespace, "namespace", "n", rootArgs.defaults.Namespace,
"the namespace scope for this operation, can be set with FLUX_SYSTEM_NAMESPACE env var")
rootCmd.RegisterFlagCompletionFunc("namespace", resourceNamesCompletionFunc(corev1.SchemeGroupVersion.WithKind("Namespace")))
rootCmd.PersistentFlags().DurationVar(&rootArgs.timeout, "timeout", 5*time.Minute, "timeout for this operation") rootCmd.PersistentFlags().DurationVar(&rootArgs.timeout, "timeout", 5*time.Minute, "timeout for this operation")
rootCmd.PersistentFlags().BoolVar(&rootArgs.verbose, "verbose", false, "print generated objects") rootCmd.PersistentFlags().BoolVar(&rootArgs.verbose, "verbose", false, "print generated objects")
rootCmd.PersistentFlags().StringVarP(&rootArgs.kubeconfig, "kubeconfig", "", "",
"absolute path to the kubeconfig file")
configureDefaultNamespace() rootCmd.PersistentFlags().StringVarP(&rootArgs.kubecontext, "context", "", "", "kubernetes context to use")
kubeconfigArgs.APIServer = nil // prevent AddFlags from configuring --server flag
kubeconfigArgs.Timeout = nil // prevent AddFlags from configuring --request-timeout flag, we have --timeout instead
kubeconfigArgs.AddFlags(rootCmd.PersistentFlags())
// Since some subcommands use the `-s` flag as a short version for `--silent`, we manually configure the server flag
// without the `-s` short version. While we're no longer on par with kubectl's flags, we maintain backwards compatibility
// on the CLI interface.
apiServer := ""
kubeconfigArgs.APIServer = &apiServer
rootCmd.PersistentFlags().StringVar(kubeconfigArgs.APIServer, "server", *kubeconfigArgs.APIServer, "The address and port of the Kubernetes API server")
rootCmd.RegisterFlagCompletionFunc("context", contextsCompletionFunc) rootCmd.RegisterFlagCompletionFunc("context", contextsCompletionFunc)
rootCmd.RegisterFlagCompletionFunc("namespace", resourceNamesCompletionFunc(corev1.SchemeGroupVersion.WithKind("Namespace")))
rootCmd.DisableAutoGenTag = true rootCmd.DisableAutoGenTag = true
rootCmd.SetOut(os.Stdout) rootCmd.SetOut(os.Stdout)
@@ -152,28 +138,30 @@ func NewRootFlags() rootFlags {
func main() { func main() {
log.SetFlags(0) log.SetFlags(0)
configureKubeconfig()
configureDefaultNamespace()
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
if err, ok := err.(*RequestError); ok {
if err.StatusCode == 1 {
logger.Warningf("%v", err)
} else {
logger.Failuref("%v", err)
}
os.Exit(err.StatusCode)
}
logger.Failuref("%v", err) logger.Failuref("%v", err)
os.Exit(1) os.Exit(1)
} }
} }
func configureKubeconfig() {
switch {
case len(rootArgs.kubeconfig) > 0:
case len(os.Getenv("KUBECONFIG")) > 0:
rootArgs.kubeconfig = os.Getenv("KUBECONFIG")
default:
if home := homeDir(); len(home) > 0 {
rootArgs.kubeconfig = filepath.Join(home, ".kube", "config")
}
}
}
func configureDefaultNamespace() { func configureDefaultNamespace() {
*kubeconfigArgs.Namespace = rootArgs.defaults.Namespace
fromEnv := os.Getenv("FLUX_SYSTEM_NAMESPACE") fromEnv := os.Getenv("FLUX_SYSTEM_NAMESPACE")
if fromEnv != "" { if fromEnv != "" && rootArgs.namespace == rootArgs.defaults.Namespace {
kubeconfigArgs.Namespace = &fromEnv rootArgs.namespace = fromEnv
} }
} }

View File

@@ -1,4 +1,3 @@
//go:build e2e
// +build e2e // +build e2e
/* /*
@@ -36,7 +35,7 @@ func TestMain(m *testing.M) {
if err != nil { if err != nil {
panic(fmt.Errorf("error creating kube manager: '%w'", err)) panic(fmt.Errorf("error creating kube manager: '%w'", err))
} }
kubeconfigArgs.KubeConfig = &testEnv.kubeConfigPath rootArgs.kubeconfig = testEnv.kubeConfigPath
// Install Flux. // Install Flux.
output, err := executeCommand("install --components-extra=image-reflector-controller,image-automation-controller") output, err := executeCommand("install --components-extra=image-reflector-controller,image-automation-controller")
@@ -55,7 +54,7 @@ func TestMain(m *testing.M) {
// Delete namespace and wait for finalisation // Delete namespace and wait for finalisation
kubectlArgs := []string{"delete", "namespace", "flux-system"} kubectlArgs := []string{"delete", "namespace", "flux-system"}
_, err = utils.ExecKubectlCommand(context.TODO(), utils.ModeStderrOS, *kubeconfigArgs.KubeConfig, *kubeconfigArgs.Context, kubectlArgs...) _, err = utils.ExecKubectlCommand(context.TODO(), utils.ModeStderrOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...)
if err != nil { if err != nil {
panic(fmt.Errorf("delete namespace error:'%w'", err)) panic(fmt.Errorf("delete namespace error:'%w'", err))
} }
@@ -67,13 +66,13 @@ func TestMain(m *testing.M) {
func setupTestNamespace(namespace string) (func(), error) { func setupTestNamespace(namespace string) (func(), error) {
kubectlArgs := []string{"create", "namespace", namespace} kubectlArgs := []string{"create", "namespace", namespace}
_, err := utils.ExecKubectlCommand(context.TODO(), utils.ModeStderrOS, *kubeconfigArgs.KubeConfig, *kubeconfigArgs.Context, kubectlArgs...) _, err := utils.ExecKubectlCommand(context.TODO(), utils.ModeStderrOS, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return func() { return func() {
kubectlArgs := []string{"delete", "namespace", namespace} kubectlArgs := []string{"delete", "namespace", namespace}
utils.ExecKubectlCommand(context.TODO(), utils.ModeCapture, *kubeconfigArgs.KubeConfig, *kubeconfigArgs.Context, kubectlArgs...) utils.ExecKubectlCommand(context.TODO(), utils.ModeCapture, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...)
}, nil }, nil
} }

View File

@@ -20,7 +20,6 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"context" "context"
"flag"
"fmt" "fmt"
"io" "io"
"os" "os"
@@ -43,9 +42,6 @@ import (
var nextNamespaceId int64 var nextNamespaceId int64
// update allows golden files to be updated based on the current output.
var update = flag.Bool("update", false, "update golden files")
// Return a unique namespace with the specified prefix, for tests to create // Return a unique namespace with the specified prefix, for tests to create
// objects that won't collide with each other. // objects that won't collide with each other.
func allocateNamespace(prefix string) string { func allocateNamespace(prefix string) string {
@@ -53,8 +49,8 @@ func allocateNamespace(prefix string) string {
return fmt.Sprintf("%s-%d", prefix, id) return fmt.Sprintf("%s-%d", prefix, id)
} }
func readYamlObjects(rdr io.Reader) ([]*unstructured.Unstructured, error) { func readYamlObjects(rdr io.Reader) ([]unstructured.Unstructured, error) {
objects := []*unstructured.Unstructured{} objects := []unstructured.Unstructured{}
reader := k8syaml.NewYAMLReader(bufio.NewReader(rdr)) reader := k8syaml.NewYAMLReader(bufio.NewReader(rdr))
for { for {
doc, err := reader.Read() doc, err := reader.Read()
@@ -69,7 +65,7 @@ func readYamlObjects(rdr io.Reader) ([]*unstructured.Unstructured, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
objects = append(objects, unstructuredObj) objects = append(objects, *unstructuredObj)
} }
return objects, nil return objects, nil
} }
@@ -100,7 +96,7 @@ func (m *testEnvKubeManager) CreateObjectFile(objectFile string, templateValues
} }
} }
func (m *testEnvKubeManager) CreateObjects(clientObjects []*unstructured.Unstructured, t *testing.T) error { func (m *testEnvKubeManager) CreateObjects(clientObjects []unstructured.Unstructured, t *testing.T) error {
for _, obj := range clientObjects { for _, obj := range clientObjects {
// First create the object then set its status if present in the // First create the object then set its status if present in the
// yaml file. Make a copy first since creating an object may overwrite // yaml file. Make a copy first since creating an object may overwrite
@@ -111,7 +107,7 @@ func (m *testEnvKubeManager) CreateObjects(clientObjects []*unstructured.Unstruc
return err return err
} }
obj.SetResourceVersion(createObj.GetResourceVersion()) obj.SetResourceVersion(createObj.GetResourceVersion())
err = m.client.Status().Update(context.Background(), obj) err = m.client.Status().Update(context.Background(), &obj)
if err != nil { if err != nil {
return err return err
} }
@@ -119,36 +115,6 @@ func (m *testEnvKubeManager) CreateObjects(clientObjects []*unstructured.Unstruc
return nil return nil
} }
func (m *testEnvKubeManager) DeleteObjectFile(objectFile string, templateValues map[string]string, t *testing.T) {
buf, err := os.ReadFile(objectFile)
if err != nil {
t.Fatalf("Error reading file '%s': %v", objectFile, err)
}
content, err := executeTemplate(string(buf), templateValues)
if err != nil {
t.Fatalf("Error evaluating template file '%s': '%v'", objectFile, err)
}
clientObjects, err := readYamlObjects(strings.NewReader(content))
if err != nil {
t.Fatalf("Error decoding yaml file '%s': %v", objectFile, err)
}
err = m.DeleteObjects(clientObjects, t)
if err != nil {
t.Logf("Error deleting test objects: '%v'", err)
}
}
func (m *testEnvKubeManager) DeleteObjects(clientObjects []*unstructured.Unstructured, t *testing.T) error {
for _, obj := range clientObjects {
err := m.client.Delete(context.Background(), obj)
if err != nil {
return err
}
}
return nil
}
func (m *testEnvKubeManager) Stop() error { func (m *testEnvKubeManager) Stop() error {
if m.testEnv == nil { if m.testEnv == nil {
return fmt.Errorf("do nothing because testEnv is nil") return fmt.Errorf("do nothing because testEnv is nil")
@@ -302,18 +268,6 @@ func assertGoldenTemplateFile(goldenFile string, templateValues map[string]strin
expectedOutput = string(goldenFileContents) expectedOutput = string(goldenFileContents)
} }
if assertErr := assertGoldenValue(expectedOutput)(output, err); assertErr != nil { if assertErr := assertGoldenValue(expectedOutput)(output, err); assertErr != nil {
// Update the golden files if comparison fails and the update flag is set.
if *update && output != "" {
// Skip update if there are template values.
if len(templateValues) > 0 {
fmt.Println("NOTE: -update flag passed but golden template files can't be updated, please update it manually")
} else {
if err := os.WriteFile(goldenFile, []byte(output), 0644); err != nil {
return fmt.Errorf("failed to update golden file '%s': %v", goldenFile, err)
}
return nil
}
}
return fmt.Errorf("Mismatch from golden file '%s': %v", goldenFile, assertErr) return fmt.Errorf("Mismatch from golden file '%s': %v", goldenFile, assertErr)
} }
return nil return nil
@@ -341,12 +295,6 @@ type cmdTestCase struct {
func (cmd *cmdTestCase) runTestCmd(t *testing.T) { func (cmd *cmdTestCase) runTestCmd(t *testing.T) {
actual, testErr := executeCommand(cmd.args) actual, testErr := executeCommand(cmd.args)
// If the cmd error is a change, discard it
if isChangeError(testErr) {
testErr = nil
}
if assertErr := cmd.assert(actual, testErr); assertErr != nil { if assertErr := cmd.assert(actual, testErr); assertErr != nil {
t.Error(assertErr) t.Error(assertErr)
} }
@@ -388,12 +336,3 @@ func resetCmdArgs() {
getArgs = GetFlags{} getArgs = GetFlags{}
secretGitArgs = NewSecretGitFlags() secretGitArgs = NewSecretGitFlags()
} }
func isChangeError(err error) bool {
if reqErr, ok := err.(*RequestError); ok {
if strings.Contains(err.Error(), "identified at least one change, exiting with non-zero exit code") && reqErr.StatusCode == 1 {
return true
}
}
return false
}

View File

@@ -1,4 +1,3 @@
//go:build unit
// +build unit // +build unit
/* /*
@@ -43,8 +42,7 @@ func TestMain(m *testing.M) {
panic(fmt.Errorf("error creating kube manager: '%w'", err)) panic(fmt.Errorf("error creating kube manager: '%w'", err))
} }
testEnv = km testEnv = km
// rootArgs.kubeconfig = testEnv.kubeConfigPath rootArgs.kubeconfig = testEnv.kubeConfigPath
kubeconfigArgs.KubeConfig = &testEnv.kubeConfigPath
// Run tests // Run tests
code := m.Run() code := m.Run()

View File

@@ -28,7 +28,6 @@ import (
// implementation can pick whichever it wants to use. // implementation can pick whichever it wants to use.
type apiType struct { type apiType struct {
kind, humanKind string kind, humanKind string
groupVersion schema.GroupVersion
} }
// adapter is an interface for a wrapper or alias from which we can // adapter is an interface for a wrapper or alias from which we can

View File

@@ -25,9 +25,8 @@ import (
// notificationv1.Receiver // notificationv1.Receiver
var receiverType = apiType{ var receiverType = apiType{
kind: notificationv1.ReceiverKind, kind: notificationv1.ReceiverKind,
humanKind: "receiver", humanKind: "receiver",
groupVersion: notificationv1.GroupVersion,
} }
type receiverAdapter struct { type receiverAdapter struct {

View File

@@ -24,7 +24,6 @@ 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/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry" "k8s.io/client-go/util/retry"
@@ -60,22 +59,13 @@ type reconcilable interface {
GetAnnotations() map[string]string GetAnnotations() map[string]string
SetAnnotations(map[string]string) SetAnnotations(map[string]string)
// this is usually implemented by GOTK types, since it's used for meta.SetResourceCondition
GetStatusConditions() *[]metav1.Condition
lastHandledReconcileRequest() string // what was the last handled reconcile request? lastHandledReconcileRequest() string // what was the last handled reconcile request?
successMessage() string // what do you want to tell people when successfully reconciled? successMessage() string // what do you want to tell people when successfully reconciled?
} }
func reconcilableConditions(object reconcilable) []metav1.Condition {
if s, ok := object.(meta.ObjectWithConditions); ok {
return s.GetConditions()
}
if s, ok := object.(oldConditions); ok {
return *s.GetStatusConditions()
}
return []metav1.Condition{}
}
func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error { func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("%s name is required", reconcile.kind) return fmt.Errorf("%s name is required", reconcile.kind)
@@ -85,13 +75,13 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Name: name, Name: name,
} }
@@ -104,9 +94,8 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
return fmt.Errorf("resource is suspended") return fmt.Errorf("resource is suspended")
} }
logger.Actionf("annotating %s %s in %s namespace", reconcile.kind, name, *kubeconfigArgs.Namespace) logger.Actionf("annotating %s %s in %s namespace", reconcile.kind, name, rootArgs.namespace)
if err := requestReconciliation(ctx, kubeClient, namespacedName, if err := requestReconciliation(ctx, kubeClient, namespacedName, reconcile.object); err != nil {
reconcile.groupVersion.WithKind(reconcile.kind)); err != nil {
return err return err
} }
logger.Successf("%s annotated", reconcile.kind) logger.Successf("%s annotated", reconcile.kind)
@@ -127,7 +116,7 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil { reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil {
return err return err
} }
readyCond := apimeta.FindStatusCondition(reconcilableConditions(reconcile.object), meta.ReadyCondition) readyCond := apimeta.FindStatusCondition(*reconcile.object.GetStatusConditions(), meta.ReadyCondition)
if readyCond == nil { if readyCond == nil {
return fmt.Errorf("status can't be determined") return fmt.Errorf("status can't be determined")
} }
@@ -146,32 +135,28 @@ func reconciliationHandled(ctx context.Context, kubeClient client.Client,
if err != nil { if err != nil {
return false, err return false, err
} }
isProgressing := apimeta.IsStatusConditionPresentAndEqual(reconcilableConditions(obj), isProgressing := apimeta.IsStatusConditionPresentAndEqual(*obj.GetStatusConditions(),
meta.ReadyCondition, metav1.ConditionUnknown) meta.ReadyCondition, metav1.ConditionUnknown)
return obj.lastHandledReconcileRequest() != lastHandledReconcileAt && !isProgressing, nil return obj.lastHandledReconcileRequest() != lastHandledReconcileAt && !isProgressing, nil
} }
} }
func requestReconciliation(ctx context.Context, kubeClient client.Client, func requestReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, gvk schema.GroupVersionKind) error { namespacedName types.NamespacedName, obj reconcilable) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
object := &metav1.PartialObjectMetadata{} if err := kubeClient.Get(ctx, namespacedName, obj.asClientObject()); err != nil {
object.SetGroupVersionKind(gvk)
object.SetName(namespacedName.Name)
object.SetNamespace(namespacedName.Namespace)
if err := kubeClient.Get(ctx, namespacedName, object); err != nil {
return err return err
} }
patch := client.MergeFrom(object.DeepCopy()) patch := client.MergeFrom(obj.deepCopyClientObject())
if ann := object.GetAnnotations(); ann == nil { if ann := obj.GetAnnotations(); ann == nil {
object.SetAnnotations(map[string]string{ obj.SetAnnotations(map[string]string{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano), meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
}) })
} else { } else {
ann[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano) ann[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
object.SetAnnotations(ann) obj.SetAnnotations(ann)
} }
return kubeClient.Patch(ctx, object, patch) return kubeClient.Patch(ctx, obj.asClientObject(), patch)
}) })
} }
@@ -183,7 +168,7 @@ func isReconcileReady(ctx context.Context, kubeClient client.Client,
return false, err return false, err
} }
if c := apimeta.FindStatusCondition(reconcilableConditions(obj), meta.ReadyCondition); c != nil { if c := apimeta.FindStatusCondition(*obj.GetStatusConditions(), meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case metav1.ConditionTrue: case metav1.ConditionTrue:
return true, nil return true, nil

View File

@@ -54,17 +54,17 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Name: name, Name: name,
} }
logger.Actionf("annotating Provider %s in %s namespace", name, *kubeconfigArgs.Namespace) logger.Actionf("annotating Provider %s in %s namespace", name, rootArgs.namespace)
var alertProvider notificationv1.Provider var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider) err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil { if err != nil {

View File

@@ -21,7 +21,7 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var reconcileHrCmd = &cobra.Command{ var reconcileHrCmd = &cobra.Command{

View File

@@ -21,7 +21,7 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var reconcileKsCmd = &cobra.Command{ var reconcileKsCmd = &cobra.Command{

View File

@@ -54,13 +54,13 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout) ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel() defer cancel()
kubeClient, err := utils.KubeClient(kubeconfigArgs) kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil { if err != nil {
return err return err
} }
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: *kubeconfigArgs.Namespace, Namespace: rootArgs.namespace,
Name: name, Name: name,
} }
@@ -74,7 +74,7 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("resource is suspended") return fmt.Errorf("resource is suspended")
} }
logger.Actionf("annotating Receiver %s in %s namespace", name, *kubeconfigArgs.Namespace) logger.Actionf("annotating Receiver %s in %s namespace", name, rootArgs.namespace)
if receiver.Annotations == nil { if receiver.Annotations == nil {
receiver.Annotations = map[string]string{ receiver.Annotations = map[string]string{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano), meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),

View File

@@ -17,11 +17,18 @@ limitations under the License.
package main package main
import ( import (
"context"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" "github.com/fluxcd/pkg/apis/meta"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var reconcileSourceBucketCmd = &cobra.Command{ var reconcileSourceBucketCmd = &cobra.Command{
@@ -41,6 +48,31 @@ func init() {
reconcileSourceCmd.AddCommand(reconcileSourceBucketCmd) reconcileSourceCmd.AddCommand(reconcileSourceBucketCmd)
} }
func isBucketReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, bucket)
if err != nil {
return false, err
}
// Confirm the state we are observing is for the current generation
if bucket.Generation != bucket.Status.ObservedGeneration {
return false, nil
}
if c := apimeta.FindStatusCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
}
func (obj bucketAdapter) lastHandledReconcileRequest() string { func (obj bucketAdapter) lastHandledReconcileRequest() string {
return obj.Status.GetLastHandledReconcileRequest() return obj.Status.GetLastHandledReconcileRequest()
} }

View File

@@ -21,7 +21,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var reconcileSourceGitCmd = &cobra.Command{ var reconcileSourceGitCmd = &cobra.Command{

View File

@@ -21,7 +21,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var reconcileSourceHelmCmd = &cobra.Command{ var reconcileSourceHelmCmd = &cobra.Command{

Some files were not shown because too many files have changed in this diff Show More