From 3f613341cba2b27421cf309730a931c70a88af3e Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Thu, 24 Jun 2021 09:56:42 +0300 Subject: [PATCH] Extend tracing to owner references Signed-off-by: Stefan Prodan --- cmd/flux/trace.go | 101 ++++++++++++++++++++++++++++++---------- internal/utils/utils.go | 13 +++--- 2 files changed, 84 insertions(+), 30 deletions(-) diff --git a/cmd/flux/trace.go b/cmd/flux/trace.go index eee3da03..0c05ae28 100644 --- a/cmd/flux/trace.go +++ b/cmd/flux/trace.go @@ -44,7 +44,16 @@ var traceCmd = &cobra.Command{ Long: `The trace command shows how an object is managed by Flux, from which source and revision it comes, and what's the latest reconciliation status.'`, Example: ` # Trace a Kubernetes Deployment - flux trace my-app --kind=deployment --api-version=apps/v1 --namespace=apps`, + flux trace my-app --kind=deployment --api-version=apps/v1 --namespace=apps + + # Trace a Kubernetes Pod + flux trace redis-master-0 --kind=pod --api-version=v1 -n redis + + # Trace a Kubernetes global object + flux trace redis --kind=namespace --api-version=v1 + + # Trace a Kubernetes custom resource + flux trace redis --kind=helmrelease --api-version=helm.toolkit.fluxcd.io/v2beta1 -n redis`, RunE: traceCmdRun, } @@ -107,7 +116,7 @@ func traceCmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to find object: %w", err) } - if ks, ok := isManagedByFlux(obj, kustomizev1.GroupVersion.Group); ok { + if ks, ok := isOwnerManagedByFlux(ctx, kubeClient, obj, kustomizev1.GroupVersion.Group); ok { report, err := traceKustomization(ctx, kubeClient, ks, obj) if err != nil { return err @@ -116,7 +125,7 @@ func traceCmdRun(cmd *cobra.Command, args []string) error { return nil } - if hr, ok := isManagedByFlux(obj, helmv2.GroupVersion.Group); ok { + if hr, ok := isOwnerManagedByFlux(ctx, kubeClient, obj, helmv2.GroupVersion.Group); ok { report, err := traceHelm(ctx, kubeClient, hr, obj) if err != nil { return err @@ -182,18 +191,22 @@ Status: Unknown GitRepository: {{.GitRepository.Name}} Namespace: {{.GitRepository.Namespace}} URL: {{.GitRepository.Spec.URL}} -{{- if .GitRepository.Spec.Reference.Branch }} -Branch: {{.GitRepository.Spec.Reference.Branch}} -{{- end }} {{- if .GitRepository.Spec.Reference.Tag }} Tag: {{.GitRepository.Spec.Reference.Tag}} {{- else if .GitRepository.Spec.Reference.SemVer }} Tag: {{.GitRepository.Spec.Reference.SemVer}} -{{- else if .GitRepository.Status.Artifact }} +{{- else if .GitRepository.Spec.Reference.Branch }} +Branch: {{.GitRepository.Spec.Reference.Branch}} +{{- end }} +{{- if .GitRepository.Status.Artifact }} Revision: {{.GitRepository.Status.Artifact.Revision}} {{- end }} {{- if .GitRepositoryReady }} +{{- if eq .GitRepositoryReady.Status "False" }} +Status: Last reconciliation failed at {{.GitRepositoryReady.LastTransitionTime}} +{{- else }} Status: Last reconciled at {{.GitRepositoryReady.LastTransitionTime}} +{{- end }} Message: {{.GitRepositoryReady.Message}} {{- else }} Status: Unknown @@ -345,29 +358,28 @@ Status: Unknown {{- end }} {{- if .GitRepository }} --- -GitRepository: {{.GitRepository.Name}} -Namespace: {{.GitRepository.Namespace}} -URL: {{.GitRepository.Spec.URL}} -{{- if .GitRepository.Spec.Reference.Branch }} -Branch: {{.GitRepository.Spec.Reference.Branch}} -{{- end }} -{{- if .GitRepository.Spec.Reference.Tag }} -Tag: {{.GitRepository.Spec.Reference.Tag}} -{{- end }} +GitRepository: {{.GitRepository.Name}} +Namespace: {{.GitRepository.Namespace}} +URL: {{.GitRepository.Spec.URL}} {{- if .GitRepository.Spec.Reference.Tag }} -Tag: {{.GitRepository.Spec.Reference.Tag}} -{{- end }} -{{- if .GitRepository.Spec.Reference.SemVer }} -Tag: {{.GitRepository.Spec.Reference.SemVer}} +Tag: {{.GitRepository.Spec.Reference.Tag}} +{{- else if .GitRepository.Spec.Reference.SemVer }} +Tag: {{.GitRepository.Spec.Reference.SemVer}} +{{- else if .GitRepository.Spec.Reference.Branch }} +Branch: {{.GitRepository.Spec.Reference.Branch}} {{- end }} {{- if .GitRepository.Status.Artifact }} -Revision: {{.GitRepository.Status.Artifact.Revision}} +Revision: {{.GitRepository.Status.Artifact.Revision}} {{- end }} {{- if .GitRepositoryReady }} -Status: Last reconciled at {{.GitRepositoryReady.LastTransitionTime}} -Message: {{.GitRepositoryReady.Message}} +{{- if eq .GitRepositoryReady.Status "False" }} +Status: Last reconciliation failed at {{.GitRepositoryReady.LastTransitionTime}} {{- else }} -Status: Unknown +Status: Last reconciled at {{.GitRepositoryReady.LastTransitionTime}} +{{- end }} +Message: {{.GitRepositoryReady.Message}} +{{- else }} +Status: Unknown {{- end }} {{- end }} ` @@ -433,3 +445,44 @@ func isManagedByFlux(obj *unstructured.Unstructured, group string) (types.Namesp } return namespacedName, true } + +func isOwnerManagedByFlux(ctx context.Context, kubeClient client.Client, obj *unstructured.Unstructured, group string) (types.NamespacedName, bool) { + if n, ok := isManagedByFlux(obj, group); ok { + return n, true + } + + namespacedName := types.NamespacedName{} + for _, reference := range obj.GetOwnerReferences() { + owner := &unstructured.Unstructured{} + gv, err := schema.ParseGroupVersion(reference.APIVersion) + if err != nil { + return namespacedName, false + } + + owner.SetGroupVersionKind(schema.GroupVersionKind{ + Group: gv.Group, + Version: gv.Version, + Kind: reference.Kind, + }) + + ownerName := types.NamespacedName{ + Namespace: obj.GetNamespace(), + Name: reference.Name, + } + + err = kubeClient.Get(ctx, ownerName, owner) + if err != nil { + return namespacedName, false + } + + if n, ok := isManagedByFlux(owner, group); ok { + return n, true + } + + if len(owner.GetOwnerReferences()) > 0 { + return isOwnerManagedByFlux(ctx, kubeClient, owner, group) + } + } + + return namespacedName, false +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 958f861f..f37249c1 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -20,9 +20,15 @@ import ( "bytes" "context" "fmt" - "github.com/olekukonko/tablewriter" "io" "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/olekukonko/tablewriter" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" @@ -34,13 +40,8 @@ import ( sigyaml "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - "os" - "os/exec" - "path/filepath" - "runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/yaml" - "strings" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" imageautov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"