From b62a70d7c1df2c67a2123957f25a92ec468bbfd7 Mon Sep 17 00:00:00 2001 From: Max Jonas Werner Date: Wed, 10 Aug 2022 14:27:23 +0200 Subject: [PATCH] Make `flux trace` work with OCIRepository * Added support for OCIRepositories to `flux trace` * Changed indentation to compensate new, longer field name "Source Revision" * Added unit tests for the new output closes #2970 Signed-off-by: Max Jonas Werner --- .../testdata/trace/helmrelease-oci.golden | 21 ++++ cmd/flux/testdata/trace/helmrelease-oci.yaml | 92 ++++++++++++++ cmd/flux/testdata/trace/helmrelease.golden | 32 ++--- cmd/flux/trace.go | 119 +++++++++++++----- cmd/flux/trace_test.go | 12 ++ 5 files changed, 228 insertions(+), 48 deletions(-) create mode 100644 cmd/flux/testdata/trace/helmrelease-oci.golden create mode 100644 cmd/flux/testdata/trace/helmrelease-oci.yaml diff --git a/cmd/flux/testdata/trace/helmrelease-oci.golden b/cmd/flux/testdata/trace/helmrelease-oci.golden new file mode 100644 index 0000000000..768c46165c --- /dev/null +++ b/cmd/flux/testdata/trace/helmrelease-oci.golden @@ -0,0 +1,21 @@ + +Object: HelmRelease/podinfo +Namespace: {{ .ns }} +Status: Managed by Flux +--- +Kustomization: infrastructure +Namespace: {{ .fluxns }} +Path: ./infrastructure +Revision: main/696f056df216eea4f9401adbee0ff744d4df390f +Status: Last reconciled at {{ .kustomizationLastReconcile }} +Message: Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f +--- +OCIRepository: flux-system +Namespace: {{ .fluxns }} +URL: oci://ghcr.io/example/repo +Tag: 1.2.3 +Revision: dbdb109711ffb3be77504d2670dbe13c24dd63d8d7f1fb489d350e5bfe930dd3 +Origin Revision: 6.1.6/450796ddb2ab6724ee1cc32a4be56da032d1cca0 +Origin Source: https://github.com/stefanprodan/podinfo.git +Status: Last reconciled at {{ .ociRepositoryLastReconcile }} +Message: stored artifact for digest 'dbdb109711ffb3be77504d2670dbe13c24dd63d8d7f1fb489d350e5bfe930dd3' diff --git a/cmd/flux/testdata/trace/helmrelease-oci.yaml b/cmd/flux/testdata/trace/helmrelease-oci.yaml new file mode 100644 index 0000000000..7c3fa2da6c --- /dev/null +++ b/cmd/flux/testdata/trace/helmrelease-oci.yaml @@ -0,0 +1,92 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .fluxns }} +--- +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .ns }} +--- +apiVersion: helm.toolkit.fluxcd.io/v2beta1 +kind: HelmRelease +metadata: + labels: + kustomize.toolkit.fluxcd.io/name: infrastructure + kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }} + name: podinfo + namespace: {{ .ns }} +spec: + chart: + spec: + chart: podinfo + sourceRef: + kind: HelmRepository + name: podinfo + namespace: {{ .fluxns }} + interval: 5m +status: + conditions: + - lastTransitionTime: "2021-07-16T15:42:20Z" + message: Release reconciliation succeeded + reason: ReconciliationSucceeded + status: "True" + type: Ready + helmChart: {{ .fluxns }}/podinfo-podinfo + lastAppliedRevision: 6.0.0 + lastAttemptedRevision: 6.0.0 + lastAttemptedValuesChecksum: c31db75d05b7515eba2eef47bd71038c74b2e531 +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 +kind: Kustomization +metadata: + name: infrastructure + namespace: {{ .fluxns }} +spec: + path: ./infrastructure + sourceRef: + kind: OCIRepository + name: flux-system + validation: client + interval: 5m + prune: false +status: + conditions: + - lastTransitionTime: "2021-08-01T04:52:56Z" + message: 'Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f' + reason: ReconciliationSucceeded + status: "True" + type: Ready + lastAppliedRevision: main/696f056df216eea4f9401adbee0ff744d4df390f +--- +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: OCIRepository +metadata: + labels: + kustomize.toolkit.fluxcd.io/name: flux-system + kustomize.toolkit.fluxcd.io/namespace: {{ .fluxns }} + name: flux-system + namespace: {{ .fluxns }} +spec: + interval: 10m0s + provider: generic + ref: + tag: 1.2.3 + timeout: 60s + url: oci://ghcr.io/example/repo +status: + artifact: + lastUpdateTime: "2022-08-10T10:07:59Z" + metadata: + org.opencontainers.image.revision: 6.1.6/450796ddb2ab6724ee1cc32a4be56da032d1cca0 + org.opencontainers.image.source: https://github.com/stefanprodan/podinfo.git + path: "example" + revision: dbdb109711ffb3be77504d2670dbe13c24dd63d8d7f1fb489d350e5bfe930dd3 + url: "example" + conditions: + - lastTransitionTime: "2021-07-20T00:48:16Z" + message: "stored artifact for digest 'dbdb109711ffb3be77504d2670dbe13c24dd63d8d7f1fb489d350e5bfe930dd3'" + reason: Succeed + status: "True" + type: Ready diff --git a/cmd/flux/testdata/trace/helmrelease.golden b/cmd/flux/testdata/trace/helmrelease.golden index cd775bad6c..310d219257 100644 --- a/cmd/flux/testdata/trace/helmrelease.golden +++ b/cmd/flux/testdata/trace/helmrelease.golden @@ -1,19 +1,19 @@ -Object: HelmRelease/podinfo -Namespace: {{ .ns }} -Status: Managed by Flux +Object: HelmRelease/podinfo +Namespace: {{ .ns }} +Status: Managed by Flux --- -Kustomization: infrastructure -Namespace: {{ .fluxns }} -Path: ./infrastructure -Revision: main/696f056df216eea4f9401adbee0ff744d4df390f -Status: Last reconciled at {{ .kustomizationLastReconcile }} -Message: Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f +Kustomization: infrastructure +Namespace: {{ .fluxns }} +Path: ./infrastructure +Revision: main/696f056df216eea4f9401adbee0ff744d4df390f +Status: Last reconciled at {{ .kustomizationLastReconcile }} +Message: Applied revision: main/696f056df216eea4f9401adbee0ff744d4df390f --- -GitRepository: flux-system -Namespace: {{ .fluxns }} -URL: ssh://git@github.com/example/repo -Branch: main -Revision: main/696f056df216eea4f9401adbee0ff744d4df390f -Status: Last reconciled at {{ .gitRepositoryLastReconcile }} -Message: Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f +GitRepository: flux-system +Namespace: {{ .fluxns }} +URL: ssh://git@github.com/example/repo +Branch: main +Revision: main/696f056df216eea4f9401adbee0ff744d4df390f +Status: Last reconciled at {{ .gitRepositoryLastReconcile }} +Message: Fetched revision: main/696f056df216eea4f9401adbee0ff744d4df390f diff --git a/cmd/flux/trace.go b/cmd/flux/trace.go index 926b60165b..1a9e0acdb6 100644 --- a/cmd/flux/trace.go +++ b/cmd/flux/trace.go @@ -37,6 +37,7 @@ import ( helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2" fluxmeta "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/oci" sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" ) @@ -219,10 +220,12 @@ func traceKustomization(ctx context.Context, kubeClient client.Client, ksName ty } ksReady := meta.FindStatusCondition(ks.Status.Conditions, fluxmeta.ReadyCondition) - var ksRepository *sourcev1.GitRepository + var gitRepository *sourcev1.GitRepository + var ociRepository *sourcev1.OCIRepository var ksRepositoryReady *metav1.Condition - if ks.Spec.SourceRef.Kind == sourcev1.GitRepositoryKind { - ksRepository = &sourcev1.GitRepository{} + switch ks.Spec.SourceRef.Kind { + case sourcev1.GitRepositoryKind: + gitRepository = &sourcev1.GitRepository{} sourceNamespace := ks.Namespace if ks.Spec.SourceRef.Namespace != "" { sourceNamespace = ks.Spec.SourceRef.Namespace @@ -230,61 +233,109 @@ func traceKustomization(ctx context.Context, kubeClient client.Client, ksName ty err = kubeClient.Get(ctx, types.NamespacedName{ Namespace: sourceNamespace, Name: ks.Spec.SourceRef.Name, - }, ksRepository) + }, gitRepository) if err != nil { return "", fmt.Errorf("failed to find GitRepository: %w", err) } - ksRepositoryReady = meta.FindStatusCondition(ksRepository.Status.Conditions, fluxmeta.ReadyCondition) + ksRepositoryReady = meta.FindStatusCondition(gitRepository.Status.Conditions, fluxmeta.ReadyCondition) + case sourcev1.OCIRepositoryKind: + ociRepository = &sourcev1.OCIRepository{} + sourceNamespace := ks.Namespace + if ks.Spec.SourceRef.Namespace != "" { + sourceNamespace = ks.Spec.SourceRef.Namespace + } + err = kubeClient.Get(ctx, types.NamespacedName{ + Namespace: sourceNamespace, + Name: ks.Spec.SourceRef.Name, + }, ociRepository) + if err != nil { + return "", fmt.Errorf("failed to find OCIRepository: %w", err) + } + ksRepositoryReady = meta.FindStatusCondition(ociRepository.Status.Conditions, fluxmeta.ReadyCondition) } var traceTmpl = ` -Object: {{.ObjectName}} +Object: {{.ObjectName}} {{- if .ObjectNamespace }} -Namespace: {{.ObjectNamespace}} +Namespace: {{.ObjectNamespace}} {{- end }} -Status: Managed by Flux +Status: Managed by Flux {{- if .Kustomization }} --- -Kustomization: {{.Kustomization.Name}} -Namespace: {{.Kustomization.Namespace}} +Kustomization: {{.Kustomization.Name}} +Namespace: {{.Kustomization.Namespace}} {{- if .Kustomization.Spec.TargetNamespace }} -Target: {{.Kustomization.Spec.TargetNamespace}} +Target: {{.Kustomization.Spec.TargetNamespace}} {{- end }} -Path: {{.Kustomization.Spec.Path}} -Revision: {{.Kustomization.Status.LastAppliedRevision}} +Path: {{.Kustomization.Spec.Path}} +Revision: {{.Kustomization.Status.LastAppliedRevision}} {{- if .KustomizationReady }} -Status: Last reconciled at {{.KustomizationReady.LastTransitionTime}} -Message: {{.KustomizationReady.Message}} +Status: Last reconciled at {{.KustomizationReady.LastTransitionTime}} +Message: {{.KustomizationReady.Message}} {{- else }} -Status: Unknown +Status: Unknown {{- end }} {{- end }} {{- if .GitRepository }} --- -GitRepository: {{.GitRepository.Name}} -Namespace: {{.GitRepository.Namespace}} -URL: {{.GitRepository.Spec.URL}} +GitRepository: {{.GitRepository.Name}} +Namespace: {{.GitRepository.Namespace}} +URL: {{.GitRepository.Spec.URL}} {{- if .GitRepository.Spec.Reference }} {{- if .GitRepository.Spec.Reference.Tag }} -Tag: {{.GitRepository.Spec.Reference.Tag}} +Tag: {{.GitRepository.Spec.Reference.Tag}} {{- else if .GitRepository.Spec.Reference.SemVer }} -Tag: {{.GitRepository.Spec.Reference.SemVer}} +Tag: {{.GitRepository.Spec.Reference.SemVer}} {{- else if .GitRepository.Spec.Reference.Branch }} -Branch: {{.GitRepository.Spec.Reference.Branch}} +Branch: {{.GitRepository.Spec.Reference.Branch}} {{- end }} {{- end }} {{- if .GitRepository.Status.Artifact }} -Revision: {{.GitRepository.Status.Artifact.Revision}} +Revision: {{.GitRepository.Status.Artifact.Revision}} {{- end }} -{{- if .GitRepositoryReady }} -{{- if eq .GitRepositoryReady.Status "False" }} -Status: Last reconciliation failed at {{.GitRepositoryReady.LastTransitionTime}} +{{- if .RepositoryReady }} +{{- if eq .RepositoryReady.Status "False" }} +Status: Last reconciliation failed at {{.RepositoryReady.LastTransitionTime}} {{- else }} -Status: Last reconciled at {{.GitRepositoryReady.LastTransitionTime}} +Status: Last reconciled at {{.RepositoryReady.LastTransitionTime}} {{- end }} -Message: {{.GitRepositoryReady.Message}} +Message: {{.RepositoryReady.Message}} {{- else }} -Status: Unknown +Status: Unknown +{{- end }} +{{- end }} +{{- if .OCIRepository }} +--- +OCIRepository: {{.OCIRepository.Name}} +Namespace: {{.OCIRepository.Namespace}} +URL: {{.OCIRepository.Spec.URL}} +{{- if .OCIRepository.Spec.Reference }} +{{- if .OCIRepository.Spec.Reference.Tag }} +Tag: {{.OCIRepository.Spec.Reference.Tag}} +{{- else if .OCIRepository.Spec.Reference.SemVer }} +Tag: {{.OCIRepository.Spec.Reference.SemVer}} +{{- else if .OCIRepository.Spec.Reference.Digest }} +Digest: {{.OCIRepository.Spec.Reference.Digest}} +{{- end }} +{{- end }} +{{- if .OCIRepository.Status.Artifact }} +Revision: {{.OCIRepository.Status.Artifact.Revision}} +{{- if .OCIRepository.Status.Artifact.Metadata }} +{{- $metadata := .OCIRepository.Status.Artifact.Metadata }} +{{- range $k, $v := .Annotations }} +{{ with (index $metadata $v) }}{{ $k }}{{ . }}{{ end }} +{{- end }} +{{- end }} +{{- end }} +{{- if .RepositoryReady }} +{{- if eq .RepositoryReady.Status "False" }} +Status: Last reconciliation failed at {{.RepositoryReady.LastTransitionTime}} +{{- else }} +Status: Last reconciled at {{.RepositoryReady.LastTransitionTime}} +{{- end }} +Message: {{.RepositoryReady.Message}} +{{- else }} +Status: Unknown {{- end }} {{- end }} ` @@ -295,14 +346,18 @@ Status: Unknown Kustomization *kustomizev1.Kustomization KustomizationReady *metav1.Condition GitRepository *sourcev1.GitRepository - GitRepositoryReady *metav1.Condition + OCIRepository *sourcev1.OCIRepository + RepositoryReady *metav1.Condition + Annotations map[string]string }{ ObjectName: obj.GetKind() + "/" + obj.GetName(), ObjectNamespace: obj.GetNamespace(), Kustomization: ks, KustomizationReady: ksReady, - GitRepository: ksRepository, - GitRepositoryReady: ksRepositoryReady, + GitRepository: gitRepository, + OCIRepository: ociRepository, + RepositoryReady: ksRepositoryReady, + Annotations: map[string]string{"Origin Source: ": oci.SourceAnnotation, "Origin Revision: ": oci.RevisionAnnotation}, } t, err := template.New("tmpl").Parse(traceTmpl) diff --git a/cmd/flux/trace_test.go b/cmd/flux/trace_test.go index 1f3dd8892f..8020e1a8ac 100644 --- a/cmd/flux/trace_test.go +++ b/cmd/flux/trace_test.go @@ -57,6 +57,18 @@ func TestTrace(t *testing.T) { "gitRepositoryLastReconcile": toLocalTime(t, "2021-07-20T00:48:16Z"), }, }, + { + "HelmRelease from OCI registry", + "trace podinfo --kind HelmRelease --api-version=helm.toolkit.fluxcd.io/v2beta1", + "testdata/trace/helmrelease-oci.yaml", + "testdata/trace/helmrelease-oci.golden", + map[string]string{ + "ns": allocateNamespace("podinfo"), + "fluxns": allocateNamespace("flux-system"), + "kustomizationLastReconcile": toLocalTime(t, "2021-08-01T04:52:56Z"), + "ociRepositoryLastReconcile": toLocalTime(t, "2021-07-20T00:48:16Z"), + }, + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) {