From 4c8e72064bcd5432ddaa9aefd537a1d228518900 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Tue, 6 Aug 2024 09:49:05 +0100 Subject: [PATCH 01/17] use t.Setenv --- cmd/main_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/main_test.go b/cmd/main_test.go index cf94d23..09b708d 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -51,7 +51,7 @@ func (c cmdTest) assert(t *testing.T) { } func TestRootCmdWithoutACluster(t *testing.T) { - _ = os.Setenv("KUBECONFIG", "/dev/null") + t.Setenv("KUBECONFIG", "/dev/null") defer plugin.SetDurationRound(func(_ interface{}) string { return "1m" })() tests := []cmdTest{ { @@ -152,7 +152,7 @@ func TestE2EAgainstVanillaMinikube(t *testing.T) { func TestAllArtifacts(t *testing.T) { defer plugin.SetDurationRound(func(_ interface{}) string { return "1m" })() - _ = os.Setenv("KUBECONFIG", "/dev/null") + t.Setenv("KUBECONFIG", "/dev/null") viper.Set("test", true) artifacts, err := filepath.Glob("../tests/artifacts/*.yaml") assert.NoError(t, err) @@ -162,7 +162,7 @@ func TestAllArtifacts(t *testing.T) { out, err := os.ReadFile(outFile) assert.NoError(t, err) test := cmdTest{ - args: []string{"-f", artifact, "--local", "--shallow"}, + args: []string{"-f", artifact, "--local", "--shallow", "--v", "255"}, stdoutEqual: string(out), } test.assert(t) // to update the out files check /tests/artifacts/README.md From 8efd6e7e0f37b23773194187557f98d1b41409c6 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Tue, 6 Aug 2024 10:57:01 +0100 Subject: [PATCH 02/17] move KubeGetFirst to engine --- pkg/plugin/plugin.go | 2 +- pkg/plugin/renderable.go | 8 +++---- pkg/plugin/template_functions_dynamic.go | 29 ++++++++++++++---------- pkg/plugin/templates_common_test.go | 2 +- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 43a8c59..6362071 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -107,7 +107,7 @@ func processObj(obj runtime.Object, engine *renderEngine) { errorPrintf(streams.ErrOut, "Failed to decode obj=%s: %s", obj, err) return } - r := newRenderableObject(out, *engine) + r := newRenderableObject(out, engine) err = r.render(streams.Out) if err != nil { _, _ = fmt.Fprintf(streams.ErrOut, "\n") diff --git a/pkg/plugin/renderable.go b/pkg/plugin/renderable.go index 380df27..0c1dbea 100644 --- a/pkg/plugin/renderable.go +++ b/pkg/plugin/renderable.go @@ -13,10 +13,10 @@ import ( kstatus "sigs.k8s.io/cli-utils/pkg/kstatus/status" ) -func newRenderableObject(obj map[string]interface{}, engine renderEngine) RenderableObject { +func newRenderableObject(obj map[string]interface{}, engine *renderEngine) RenderableObject { r := RenderableObject{ Unstructured: unstructured.Unstructured{Object: obj}, - engine: &engine, + engine: engine, Config: viper.GetViper(), } return r @@ -40,8 +40,8 @@ func (r RenderableObject) KStatus() *kstatus.Result { return result } -func (r RenderableObject) newRenderableObject(obj map[string]interface{}) RenderableObject { - return newRenderableObject(obj, *r.engine) +func (e *renderEngine) newRenderableObject(obj map[string]interface{}) RenderableObject { + return newRenderableObject(obj, e) } func (r RenderableObject) String() string { diff --git a/pkg/plugin/template_functions_dynamic.go b/pkg/plugin/template_functions_dynamic.go index 50dadc7..6d5f6d2 100644 --- a/pkg/plugin/template_functions_dynamic.go +++ b/pkg/plugin/template_functions_dynamic.go @@ -44,7 +44,7 @@ func (r RenderableObject) getCreationTimestampSortedRenderableObjects(resourceIn var out []RenderableObject for _, obj := range sortedResourceInfosToRuntimeObjects(resourceInfos) { unstructuredObj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) - nr := r.newRenderableObject(unstructuredObj) + nr := r.engine.newRenderableObject(unstructuredObj) klog.V(5).InfoS("KubeGet matched object", "object", nr) out = append(out, nr) } @@ -62,16 +62,21 @@ func sortedResourceInfosToRuntimeObjects(resourceInfos []*resource.Info) []runti // KubeGetFirst returns a new RenderableObject with a nil Object when no object found. func (r RenderableObject) KubeGetFirst(namespace string, args ...string) RenderableObject { - nr := r.newRenderableObject(nil) + return r.engine.KubeGetFirst(namespace, args...) +} + +// KubeGetFirst returns a new RenderableObject with a nil Object when no object found. +func (e *renderEngine) KubeGetFirst(namespace string, args ...string) RenderableObject { + nr := e.newRenderableObject(nil) if viper.GetBool("shallow") { return nr } klog.V(5).InfoS("called template method KubeGetFirst", - "r", r, "namespace", namespace, "args", args) - resourceInfos, err := r.engine.getResourceQueryInfos(namespace, args) + "namespace", namespace, "args", args) + resourceInfos, err := e.getResourceQueryInfos(namespace, args) if err != nil { klog.V(3).ErrorS(err, "getResourceQueryInfos failed", - "r", r, "namespace", namespace, "args", args) + "namespace", namespace, "args", args) return nr } if len(resourceInfos) >= 1 { @@ -80,7 +85,7 @@ func (r RenderableObject) KubeGetFirst(namespace string, args ...string) Rendera nr.Object = unstructuredObj } else { klog.V(3).InfoS("KubeGetFirst returning empty", - "r", r, "namespace", namespace, "args", args) + "namespace", namespace, "args", args) } return nr } @@ -109,7 +114,7 @@ func (r RenderableObject) KubeGetByLabelsMap(namespace, resourceType string, lab } func (r RenderableObject) KubeGetEvents() RenderableObject { - nr := r.newRenderableObject(nil) + nr := r.engine.newRenderableObject(nil) if viper.GetBool("shallow") { return nr } @@ -142,7 +147,7 @@ func (r RenderableObject) KubeGetResourcesOwnedOf(resourceOrKind string) (out [] List(context.TODO(), metav1.ListOptions{}) for _, controllerRevision := range controllerRevisions.Items { if doesOwnerMatch(r.Unstructured, controllerRevision) { - out = append(out, r.newRenderableObject(controllerRevision.Object)) + out = append(out, r.engine.newRenderableObject(controllerRevision.Object)) } } return @@ -210,7 +215,7 @@ func (r RenderableObject) KubeGetIngressesMatchingService(namespace, svcName str if doesIngressUseService(ing, svcName) { ing.SetGroupVersionKind(netv1.SchemeGroupVersion.WithKind("Ingress")) ingUnstructured, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&ing) - out = append(out, r.newRenderableObject(ingUnstructured)) + out = append(out, r.engine.newRenderableObject(ingUnstructured)) } } return @@ -250,7 +255,7 @@ func (r RenderableObject) KubeGetServicesMatchingLabels(namespace string, labels if doesServiceMatchLabels(svc, castedLabels) { svc.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Service")) svcUnstructured, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&svc) - out = append(out, r.newRenderableObject(svcUnstructured)) + out = append(out, r.engine.newRenderableObject(svcUnstructured)) } } return out @@ -284,7 +289,7 @@ func (r RenderableObject) KubeGetServicesMatchingPod(namespace, podName string) } svc.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Service")) svcUnstructured, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&svc) - out = append(out, r.newRenderableObject(svcUnstructured)) + out = append(out, r.engine.newRenderableObject(svcUnstructured)) } } return out @@ -381,7 +386,7 @@ func (r RenderableObject) kubeGetNonTerminatedPodsOnTheNode(nodeName string) (po } for _, pod := range nodeNonTerminatedPodsList.Items { unstructuredPod, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&pod) - nr := r.newRenderableObject(unstructuredPod) + nr := r.engine.newRenderableObject(unstructuredPod) podList = append(podList, nr) } return podList, nil diff --git a/pkg/plugin/templates_common_test.go b/pkg/plugin/templates_common_test.go index 3eba1fa..b02d9e0 100644 --- a/pkg/plugin/templates_common_test.go +++ b/pkg/plugin/templates_common_test.go @@ -13,7 +13,7 @@ func checkTemplate(t *testing.T, templateName string, obj map[string]interface{} tmpl, _ := getTemplate() e, _ := newRenderEngine(util.NewFactory(genericclioptions.NewTestConfigFlags()), genericiooptions.NewTestIOStreamsDiscard()) e.Template = *tmpl - r := newRenderableObject(obj, *e) + r := newRenderableObject(obj, e) var objToPassTemplate interface{} if useRenderable { objToPassTemplate = r From 49adb79679bfc5ca491638294de8f91be69b6df6 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Tue, 6 Aug 2024 10:59:03 +0100 Subject: [PATCH 03/17] add input.Object type --- pkg/input/input.go | 39 ++++++++++++++++++++++-- pkg/plugin/render_engine.go | 6 ---- pkg/plugin/template_functions_dynamic.go | 34 ++++++++------------- 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/pkg/input/input.go b/pkg/input/input.go index c7961a4..3cd4cb9 100644 --- a/pkg/input/input.go +++ b/pkg/input/input.go @@ -1,8 +1,11 @@ package input import ( + "sort" + "github.com/spf13/viper" "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" @@ -13,8 +16,31 @@ type ResourceRepo struct { f util.Factory } -func NewResourceRepo(factory util.Factory) ResourceRepo { +type Object map[string]interface{} + +func (u Object) creationTimestamp() string { + m, ok := u["metadata"].(map[string]string) + if !ok { + return "" + } + return m["creationTimestamp"] +} + +type Objects []Object + +func (u Objects) Len() int { + return len(u) +} + +func (u Objects) Less(i, j int) bool { + return u[i].creationTimestamp() < u[j].creationTimestamp() +} +func (u Objects) Swap(i, j int) { + u[i], u[j] = u[j], u[i] +} + +func NewResourceRepo(factory util.Factory) ResourceRepo { return ResourceRepo{ f: factory, } @@ -57,12 +83,19 @@ func (r *ResourceRepo) CLIQueryResults(args []string) *resource.Result { return builder.Do() } -func (r *ResourceRepo) ResourceInfos(namespace string, args []string, labelSelector string) ([]*resource.Info, error) { +func (r *ResourceRepo) Objects(namespace string, args []string, labelSelector string) (Objects, error) { builder := r.newBaseBuilder(). NamespaceParam(namespace). ResourceTypeOrNameArgs(true, args...). LabelSelectorParam(labelSelector) - return builder.Do().Infos() + infos, err := builder.Do().Infos() + unstructuredObjects := Objects{} + for _, info := range infos { + unstructuredObj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(info.Object) // TODO: handle error + unstructuredObjects = append(unstructuredObjects, unstructuredObj) + } + sort.Sort(unstructuredObjects) + return unstructuredObjects, err } func (r *ResourceRepo) ToRESTMapper() (meta.RESTMapper, error) { diff --git a/pkg/plugin/render_engine.go b/pkg/plugin/render_engine.go index 0e66f48..23fd33f 100644 --- a/pkg/plugin/render_engine.go +++ b/pkg/plugin/render_engine.go @@ -11,7 +11,6 @@ import ( "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/cli-runtime/pkg/genericiooptions" - "k8s.io/cli-runtime/pkg/resource" "k8s.io/klog/v2" "k8s.io/kubectl/pkg/cmd/util" @@ -131,8 +130,3 @@ func (e *renderEngine) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, return mapping, nil } - -func (e renderEngine) getResourceQueryInfos(namespace string, args []string) ([]*resource.Info, error) { - klog.V(5).InfoS("getResourceQueryInfos", "namespace", namespace, "args", args) - return e.repo.ResourceInfos(namespace, args, "") -} diff --git a/pkg/plugin/template_functions_dynamic.go b/pkg/plugin/template_functions_dynamic.go index 6d5f6d2..613dbc3 100644 --- a/pkg/plugin/template_functions_dynamic.go +++ b/pkg/plugin/template_functions_dynamic.go @@ -19,13 +19,13 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes/scheme" "k8s.io/klog/v2" "k8s.io/kubectl/pkg/cmd/events" - "k8s.io/kubectl/pkg/cmd/get" "k8s.io/kubectl/pkg/polymorphichelpers" + + "github.com/bergerx/kubectl-status/pkg/input" ) func (r RenderableObject) KubeGet(namespace string, args ...string) (out []RenderableObject) { @@ -33,16 +33,16 @@ func (r RenderableObject) KubeGet(namespace string, args ...string) (out []Rende return } klog.V(5).InfoS("processing KubeGet", "r", r, "namespace", namespace, "args", args) - resourceInfos, err := r.engine.getResourceQueryInfos(namespace, args) + objects, err := r.engine.repo.Objects(namespace, args, "") if err != nil { klog.V(3).ErrorS(err, "ignoring resource error", "r", r, "namespace", namespace, "args", args) } - return r.getCreationTimestampSortedRenderableObjects(resourceInfos) + return r.getCreationTimestampSortedRenderableObjects(objects) } -func (r RenderableObject) getCreationTimestampSortedRenderableObjects(resourceInfos []*resource.Info) []RenderableObject { +func (r RenderableObject) getCreationTimestampSortedRenderableObjects(objects input.Objects) []RenderableObject { var out []RenderableObject - for _, obj := range sortedResourceInfosToRuntimeObjects(resourceInfos) { + for _, obj := range objects { unstructuredObj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) nr := r.engine.newRenderableObject(unstructuredObj) klog.V(5).InfoS("KubeGet matched object", "object", nr) @@ -51,15 +51,6 @@ func (r RenderableObject) getCreationTimestampSortedRenderableObjects(resourceIn return out } -func sortedResourceInfosToRuntimeObjects(resourceInfos []*resource.Info) []runtime.Object { - runtimeObjList := make([]runtime.Object, len(resourceInfos)) - for i := range resourceInfos { - runtimeObjList[i] = resourceInfos[i].Object - } - _ = get.NewRuntimeSorter(runtimeObjList, ".metadata.creationTimestamp").Sort() - return runtimeObjList -} - // KubeGetFirst returns a new RenderableObject with a nil Object when no object found. func (r RenderableObject) KubeGetFirst(namespace string, args ...string) RenderableObject { return r.engine.KubeGetFirst(namespace, args...) @@ -73,16 +64,15 @@ func (e *renderEngine) KubeGetFirst(namespace string, args ...string) Renderable } klog.V(5).InfoS("called template method KubeGetFirst", "namespace", namespace, "args", args) - resourceInfos, err := e.getResourceQueryInfos(namespace, args) + objects, err := e.repo.Objects(namespace, args, "") if err != nil { klog.V(3).ErrorS(err, "getResourceQueryInfos failed", "namespace", namespace, "args", args) return nr } - if len(resourceInfos) >= 1 { - first := resourceInfos[0] - unstructuredObj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(first.Object) - nr.Object = unstructuredObj + if len(objects) >= 1 { + first := objects[0] + nr.Object = first } else { klog.V(3).InfoS("KubeGetFirst returning empty", "namespace", namespace, "args", args) @@ -104,13 +94,13 @@ func (r RenderableObject) KubeGetByLabelsMap(namespace, resourceType string, lab labelPairs = append(labelPairs, fmt.Sprintf("%s=%s", k, v)) } selector := strings.Join(labelPairs, ",") - resourceInfos, err := r.engine.repo.ResourceInfos(namespace, []string{resourceType}, selector) + unstructuredObjects, err := r.engine.repo.Objects(namespace, []string{resourceType}, selector) if err != nil { klog.V(3).ErrorS(err, "error querying labels", "r", r, "namespace", namespace, "labels", labels) return } - return r.getCreationTimestampSortedRenderableObjects(resourceInfos) + return r.getCreationTimestampSortedRenderableObjects(unstructuredObjects) } func (r RenderableObject) KubeGetEvents() RenderableObject { From 2c8d41cebb14ef45a3155c864d06f30f32519a63 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Wed, 7 Aug 2024 08:33:43 +0100 Subject: [PATCH 04/17] move MappingFor to repo --- pkg/input/input.go | 49 +++++++++++++++++++++++ pkg/plugin/render_engine.go | 50 ------------------------ pkg/plugin/template_functions_dynamic.go | 4 +- 3 files changed, 51 insertions(+), 52 deletions(-) diff --git a/pkg/input/input.go b/pkg/input/input.go index 3cd4cb9..8dbc88e 100644 --- a/pkg/input/input.go +++ b/pkg/input/input.go @@ -1,11 +1,13 @@ package input import ( + "fmt" "sort" "github.com/spf13/viper" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" @@ -109,3 +111,50 @@ func (r *ResourceRepo) KubernetesClientSet() (*kubernetes.Clientset, error) { func (r *ResourceRepo) DynamicClient() (dynamic.Interface, error) { return r.f.DynamicClient() } + +// This is a modified copy of resource.Builder's mappingFor method. +func (r *ResourceRepo) MappingFor(resourceOrKindArg string) (*meta.RESTMapping, error) { + fullySpecifiedGVR, groupResource := schema.ParseResourceArg(resourceOrKindArg) + gvk := schema.GroupVersionKind{} + restMapper, err := r.ToRESTMapper() + if err != nil { + return nil, err + } + + if fullySpecifiedGVR != nil { + gvk, _ = restMapper.KindFor(*fullySpecifiedGVR) + } + if gvk.Empty() { + gvk, _ = restMapper.KindFor(groupResource.WithVersion("")) + } + if !gvk.Empty() { + return restMapper.RESTMapping(gvk.GroupKind(), gvk.Version) + } + + fullySpecifiedGVK, groupKind := schema.ParseKindArg(resourceOrKindArg) + if fullySpecifiedGVK == nil { + gvk := groupKind.WithVersion("") + fullySpecifiedGVK = &gvk + } + + if !fullySpecifiedGVK.Empty() { + if mapping, err := restMapper.RESTMapping(fullySpecifiedGVK.GroupKind(), fullySpecifiedGVK.Version); err == nil { + return mapping, nil + } + } + + mapping, err := restMapper.RESTMapping(groupKind, gvk.Version) + if err != nil { + // if we error out here, it is because we could not match a resource or a kind + // for the given argument. To maintain consistency with previous behavior, + // announce that a resource type could not be found. + // if the error is _not_ a *meta.NoKindMatchError, then we had trouble doing discovery, + // so we should return the original error since it may help a user diagnose what is actually wrong + if meta.IsNoMatchError(err) { + return nil, fmt.Errorf("the server doesn't have a resource type %q", groupResource.Resource) + } + return nil, err + } + + return mapping, nil +} diff --git a/pkg/plugin/render_engine.go b/pkg/plugin/render_engine.go index 23fd33f..766520a 100644 --- a/pkg/plugin/render_engine.go +++ b/pkg/plugin/render_engine.go @@ -2,14 +2,11 @@ package plugin import ( "embed" - "fmt" "os" "path/filepath" "text/template" "github.com/go-sprout/sprout" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/cli-runtime/pkg/genericiooptions" "k8s.io/klog/v2" "k8s.io/kubectl/pkg/cmd/util" @@ -83,50 +80,3 @@ func findTemplateName(tmpl template.Template, kind string) string { } return kind } - -// This is a modified copy of resource.Builder's mappingFor method. -func (e *renderEngine) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, error) { - fullySpecifiedGVR, groupResource := schema.ParseResourceArg(resourceOrKindArg) - gvk := schema.GroupVersionKind{} - restMapper, err := e.repo.ToRESTMapper() - if err != nil { - return nil, err - } - - if fullySpecifiedGVR != nil { - gvk, _ = restMapper.KindFor(*fullySpecifiedGVR) - } - if gvk.Empty() { - gvk, _ = restMapper.KindFor(groupResource.WithVersion("")) - } - if !gvk.Empty() { - return restMapper.RESTMapping(gvk.GroupKind(), gvk.Version) - } - - fullySpecifiedGVK, groupKind := schema.ParseKindArg(resourceOrKindArg) - if fullySpecifiedGVK == nil { - gvk := groupKind.WithVersion("") - fullySpecifiedGVK = &gvk - } - - if !fullySpecifiedGVK.Empty() { - if mapping, err := restMapper.RESTMapping(fullySpecifiedGVK.GroupKind(), fullySpecifiedGVK.Version); err == nil { - return mapping, nil - } - } - - mapping, err := restMapper.RESTMapping(groupKind, gvk.Version) - if err != nil { - // if we error out here, it is because we could not match a resource or a kind - // for the given argument. To maintain consistency with previous behavior, - // announce that a resource type could not be found. - // if the error is _not_ a *meta.NoKindMatchError, then we had trouble doing discovery, - // so we should return the original error since it may help a user diagnose what is actually wrong - if meta.IsNoMatchError(err) { - return nil, fmt.Errorf("the server doesn't have a resource type %q", groupResource.Resource) - } - return nil, err - } - - return mapping, nil -} diff --git a/pkg/plugin/template_functions_dynamic.go b/pkg/plugin/template_functions_dynamic.go index 613dbc3..a4abbb3 100644 --- a/pkg/plugin/template_functions_dynamic.go +++ b/pkg/plugin/template_functions_dynamic.go @@ -129,7 +129,7 @@ func (r RenderableObject) KubeGetResourcesOwnedOf(resourceOrKind string) (out [] return } klog.V(5).InfoS("called template method KubeGetResourcesOwnedOf", "r", r) - restMapper, _ := r.engine.mappingFor(resourceOrKind) + restMapper, _ := r.engine.repo.MappingFor(resourceOrKind) dynamicInterface, _ := r.engine.repo.DynamicClient() controllerRevisions, _ := dynamicInterface. Resource(restMapper.Resource). @@ -400,7 +400,7 @@ func (r RenderableObject) KubeGetUnifiedDiffString(resourceOrKind, namespace, na } func (r RenderableObject) kubeGetUnifiedDiffString(resourceOrKind, namespace, nameA, nameB string) (string, error) { - controllerRevisionMapping, err := r.engine.mappingFor(resourceOrKind) + controllerRevisionMapping, err := r.engine.repo.MappingFor(resourceOrKind) if err != nil { klog.V(3).ErrorS(err, "failed to get mapping", "resourceOrKind", resourceOrKind) return "", err From 5fd76515d660c6f902bb0b0dae86dce40bafd0bc Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Wed, 7 Aug 2024 08:53:52 +0100 Subject: [PATCH 05/17] add new testcase for multiple resource query --- tests/artifacts/README.md | 15 +++ tests/artifacts/node-and-service.out | 16 +++ tests/artifacts/node-and-service.yaml | 153 ++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 tests/artifacts/node-and-service.out create mode 100644 tests/artifacts/node-and-service.yaml diff --git a/tests/artifacts/README.md b/tests/artifacts/README.md index c75b64b..e879fde 100644 --- a/tests/artifacts/README.md +++ b/tests/artifacts/README.md @@ -14,3 +14,18 @@ for yaml in ./tests/artifacts/*.yaml; do go run ./cmd --time-hack-ago -f ${yaml} --local --shallow > ${out} done ``` + +# Adding a new case + +First generate the artifact file using a command like this: + +```bash +cmd="" # the command line parameters for generating the new manifest, e.g.: -n default node,service +file="" # filename for the new artifact file to be stored, e.g. node-and-service + +cd ../.. +kubectl get -o yaml ${cmd} > tests/artifacts/${file}.yaml +go run ./cmd --time-hack-ago ${cmd} --shallow > tests/artifacts/${file}.out +make test +git add tests/artifacts/${file}.yaml tests/artifacts/${file}.out +``` diff --git a/tests/artifacts/node-and-service.out b/tests/artifacts/node-and-service.out new file mode 100644 index 0000000..b87aaa4 --- /dev/null +++ b/tests/artifacts/node-and-service.out @@ -0,0 +1,16 @@ + +Node/minikube, created 1m ago + linux Ubuntu 22.04.4 LTS (amd64), kernel 6.6.41-1-MANJARO, kubelet v1.30.0, kube-proxy v1.30.0 + images 8 + Current: Resource is Ready + MemoryPressure KubeletHasSufficientMemory, kubelet has sufficient memory available for 1m + DiskPressure KubeletHasNoDiskPressure, kubelet has no disk pressure for 1m + PIDPressure KubeletHasSufficientPID, kubelet has sufficient PID available for 1m + Ready KubeletReady, kubelet is posting ready status for 1m + addresses: InternalIP=192.168.49.2 Hostname=minikube + allocatable: pods:110, cpu:16, mem:33.3GB, ephemeral-storage:997.3GB + capacity: pods:110, cpu:16, mem:33.3GB, ephemeral-storage:997.3GB + +Service/kubernetes -n default, created 1m ago + Current: Service is ready + Missing Endpoint: Service has no matching endpoint. diff --git a/tests/artifacts/node-and-service.yaml b/tests/artifacts/node-and-service.yaml new file mode 100644 index 0000000..10d55bf --- /dev/null +++ b/tests/artifacts/node-and-service.yaml @@ -0,0 +1,153 @@ +apiVersion: v1 +items: +- apiVersion: v1 + kind: Node + metadata: + annotations: + kubeadm.alpha.kubernetes.io/cri-socket: unix:///var/run/cri-dockerd.sock + node.alpha.kubernetes.io/ttl: "0" + volumes.kubernetes.io/controller-managed-attach-detach: "true" + creationTimestamp: "2024-08-06T08:36:21Z" + labels: + beta.kubernetes.io/arch: amd64 + beta.kubernetes.io/os: linux + kubernetes.io/arch: amd64 + kubernetes.io/hostname: minikube + kubernetes.io/os: linux + minikube.k8s.io/commit: 5883c09216182566a63dff4c326a6fc9ed2982ff-dirty + minikube.k8s.io/name: minikube + minikube.k8s.io/primary: "true" + minikube.k8s.io/updated_at: 2024_08_06T09_36_23_0700 + minikube.k8s.io/version: v1.33.1 + node-role.kubernetes.io/control-plane: "" + node.kubernetes.io/exclude-from-external-load-balancers: "" + name: minikube + resourceVersion: "8978" + uid: 8319502f-5189-46fc-a54f-184f79a1cff5 + spec: + podCIDR: 10.244.0.0/24 + podCIDRs: + - 10.244.0.0/24 + status: + addresses: + - address: 192.168.49.2 + type: InternalIP + - address: minikube + type: Hostname + allocatable: + cpu: "16" + ephemeral-storage: 973983276Ki + hugepages-1Gi: "0" + hugepages-2Mi: "0" + memory: 32587744Ki + pods: "110" + capacity: + cpu: "16" + ephemeral-storage: 973983276Ki + hugepages-1Gi: "0" + hugepages-2Mi: "0" + memory: 32587744Ki + pods: "110" + conditions: + - lastHeartbeatTime: "2024-08-07T07:45:09Z" + lastTransitionTime: "2024-08-06T08:36:20Z" + message: kubelet has sufficient memory available + reason: KubeletHasSufficientMemory + status: "False" + type: MemoryPressure + - lastHeartbeatTime: "2024-08-07T07:45:09Z" + lastTransitionTime: "2024-08-06T08:36:20Z" + message: kubelet has no disk pressure + reason: KubeletHasNoDiskPressure + status: "False" + type: DiskPressure + - lastHeartbeatTime: "2024-08-07T07:45:09Z" + lastTransitionTime: "2024-08-06T08:36:20Z" + message: kubelet has sufficient PID available + reason: KubeletHasSufficientPID + status: "False" + type: PIDPressure + - lastHeartbeatTime: "2024-08-07T07:45:09Z" + lastTransitionTime: "2024-08-06T08:36:21Z" + message: kubelet is posting ready status + reason: KubeletReady + status: "True" + type: Ready + daemonEndpoints: + kubeletEndpoint: + Port: 10250 + images: + - names: + - registry.k8s.io/etcd@sha256:44a8e24dcbba3470ee1fee21d5e88d128c936e9b55d4bc51fbef8086f8ed123b + - registry.k8s.io/etcd:3.5.12-0 + sizeBytes: 149347661 + - names: + - registry.k8s.io/kube-apiserver@sha256:6b8e197b2d39c321189a475ac755a77896e34b56729425590fbc99f3a96468a3 + - registry.k8s.io/kube-apiserver:v1.30.0 + sizeBytes: 116552324 + - names: + - registry.k8s.io/kube-controller-manager@sha256:5f52f00f17d5784b5ca004dffca59710fa1a9eec8d54cebdf9433a1d134150fe + - registry.k8s.io/kube-controller-manager:v1.30.0 + sizeBytes: 111113187 + - names: + - registry.k8s.io/kube-proxy@sha256:ec532ff47eaf39822387e51ec73f1f2502eb74658c6303319db88d2c380d0210 + - registry.k8s.io/kube-proxy:v1.30.0 + sizeBytes: 84675401 + - names: + - registry.k8s.io/kube-scheduler@sha256:2353c3a1803229970fcb571cffc9b2f120372350e01c7381b4b650c4a02b9d67 + - registry.k8s.io/kube-scheduler:v1.30.0 + sizeBytes: 61969366 + - names: + - registry.k8s.io/coredns/coredns@sha256:1eeb4c7316bacb1d4c8ead65571cd92dd21e27359f0d4917f1a5822a73b75db1 + - registry.k8s.io/coredns/coredns:v1.11.1 + sizeBytes: 59820619 + - names: + - gcr.io/k8s-minikube/storage-provisioner@sha256:18eb69d1418e854ad5a19e399310e52808a8321e4c441c1dddad8977a0d7a944 + - gcr.io/k8s-minikube/storage-provisioner:v5 + sizeBytes: 31465472 + - names: + - registry.k8s.io/pause@sha256:7031c1b283388d2c2e09b57badb803c05ebed362dc88d84b480cc47f72a21097 + - registry.k8s.io/pause:3.9 + sizeBytes: 743952 + nodeInfo: + architecture: amd64 + bootID: 347b6358-8163-4429-b24d-c5b7841e4b41 + containerRuntimeVersion: docker://26.1.1 + kernelVersion: 6.6.41-1-MANJARO + kubeProxyVersion: v1.30.0 + kubeletVersion: v1.30.0 + machineID: d6f70d223fef420cbd26ccac4bd0412b + operatingSystem: linux + osImage: Ubuntu 22.04.4 LTS + systemUUID: d91167d3-7a3a-499e-aaaa-6c952fc10a74 +- apiVersion: v1 + kind: Service + metadata: + creationTimestamp: "2024-08-06T08:36:22Z" + labels: + component: apiserver + provider: kubernetes + name: kubernetes + namespace: default + resourceVersion: "198" + uid: ef2ff9cf-09e9-4bd6-ba01-3daf3e8886c6 + spec: + clusterIP: 10.96.0.1 + clusterIPs: + - 10.96.0.1 + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - name: https + port: 443 + protocol: TCP + targetPort: 8443 + sessionAffinity: None + type: ClusterIP + status: + loadBalancer: {} +kind: List +metadata: + resourceVersion: "" From 352ffaac0b5fbc4ef619eafc8dc4ece1d7c6a7b6 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Fri, 9 Aug 2024 15:27:31 +0100 Subject: [PATCH 06/17] unexport DynamicClient --- cmd/main_test.go | 74 ++++++++++++++++++++++++ pkg/input/input.go | 71 +++++++++++++++++++++-- pkg/plugin/template_functions_dynamic.go | 42 ++++++-------- 3 files changed, 156 insertions(+), 31 deletions(-) diff --git a/cmd/main_test.go b/cmd/main_test.go index 09b708d..5301062 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "os" "os/exec" "path" @@ -11,6 +12,10 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" "k8s.io/klog/v2" "github.com/bergerx/kubectl-status/pkg/plugin" @@ -205,3 +210,72 @@ func startMinikube(t *testing.T, clusterName string) (deleteMinikube func()) { } } } + +func TestE2EDynamicManifests(t *testing.T) { + if os.Getenv("RUN_E2E_TESTS") != "true" { + t.Skip("Skipping e2e test") + } + if os.Getenv("ASSUME_MINIKUBE_IS_CONFIGURED") != "true" { + defer startMinikube(t, "kubectl-status-e2e")() + } + defer plugin.SetDurationRound(func(_ interface{}) string { return "1m" })() + viper.Set("test", true) + kubeconfigPath := os.Getenv("KUBECONFIG") + if kubeconfigPath == "" { + homeDir := os.Getenv("HOME") + kubeconfigPath = filepath.Join(homeDir, ".kube", "config") + } + config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + if err != nil { + t.Fatal(err) + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + t.Fatal(err) + } + t.Run("owners should be included with deep", func(t *testing.T) { + owner := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "owner", + Namespace: "default", + }, + } + owner, err := clientset.CoreV1().Secrets("default").Create(context.TODO(), owner, metav1.CreateOptions{}) + defer clientset.CoreV1().Secrets("default").Delete(context.TODO(), owner.Name, metav1.DeleteOptions{}) + assert.NoError(t, err) + // Create the owned secret with owner reference + owned := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "owned", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "v1", + Kind: "Secret", + Name: "owner", + UID: owner.UID, + }, + }, + }, + } + _, err = clientset.CoreV1().Secrets("default").Create(context.TODO(), owned, metav1.CreateOptions{}) + defer clientset.CoreV1().Secrets("default").Delete(context.TODO(), owned.Name, metav1.DeleteOptions{}) + assert.NoError(t, err) + + test := cmdTest{ + args: []string{"secret/owned", "--deep", "--v", "255"}, + stdoutRegex: `(?ms) +Secret\/owned -n default, created 1m ago by Secret/owner + Current: Resource is always ready + Known\/recorded manage events: + 1m ago Updated by [^ ]+ \(metadata, type\) + Owners: + Secret\/owner -n default, created 1m ago + Current: Resource is always ready + Known\/recorded manage events: + 1m ago Updated by [^ ]+ \(type\) +`, + } + test.assert(t) // to update the out files check /tests/artifacts/README.md + }) +} diff --git a/pkg/input/input.go b/pkg/input/input.go index 8dbc88e..606be83 100644 --- a/pkg/input/input.go +++ b/pkg/input/input.go @@ -1,11 +1,14 @@ package input import ( + "context" "fmt" "sort" "github.com/spf13/viper" "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/cli-runtime/pkg/resource" @@ -15,7 +18,8 @@ import ( ) type ResourceRepo struct { - f util.Factory + f util.Factory + client dynamic.Interface } type Object map[string]interface{} @@ -28,6 +32,10 @@ func (u Object) creationTimestamp() string { return m["creationTimestamp"] } +func (u Object) Unstructured() *unstructured.Unstructured { + return &unstructured.Unstructured{Object: u} +} + type Objects []Object func (u Objects) Len() int { @@ -100,7 +108,7 @@ func (r *ResourceRepo) Objects(namespace string, args []string, labelSelector st return unstructuredObjects, err } -func (r *ResourceRepo) ToRESTMapper() (meta.RESTMapper, error) { +func (r *ResourceRepo) toRESTMapper() (meta.RESTMapper, error) { return r.f.ToRESTMapper() } @@ -108,15 +116,66 @@ func (r *ResourceRepo) KubernetesClientSet() (*kubernetes.Clientset, error) { return r.f.KubernetesClientSet() } -func (r *ResourceRepo) DynamicClient() (dynamic.Interface, error) { - return r.f.DynamicClient() +func (r *ResourceRepo) dynamicClient() (dynamic.Interface, error) { + if r.client == nil { + var err error + r.client, err = r.f.DynamicClient() + if err != nil { + return nil, err + } + } + return r.client, nil +} + +func (r *ResourceRepo) DynamicObject(gvr schema.GroupVersionResource, namespace string, name string) (Object, error) { + dynamicClient, err := r.dynamicClient() + if err != nil { + return nil, err + } + u, err := dynamicClient.Resource(gvr).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + object, err := runtime.DefaultUnstructuredConverter.ToUnstructured(u.Object) + if err != nil { + return nil, err + } + return object, nil +} + +func (r *ResourceRepo) DynamicObjects(gvr schema.GroupVersionResource, namespace string) (Objects, error) { + dynamicClient, err := r.dynamicClient() + if err != nil { + return nil, err + } + unstructuredList, err := dynamicClient.Resource(gvr).Namespace(namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + var objects Objects + for _, unstructuredObj := range unstructuredList.Items { + unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(unstructuredObj.Object) + if err != nil { + return nil, err + } + objects = append(objects, unstructuredObj) + } + return objects, nil +} + +func (r *ResourceRepo) GVRFor(resourceOrKindArg string) (schema.GroupVersionResource, error) { + mapping, err := r.mappingFor(resourceOrKindArg) + if err != nil { + return schema.GroupVersionResource{}, err + } + return mapping.Resource, nil } // This is a modified copy of resource.Builder's mappingFor method. -func (r *ResourceRepo) MappingFor(resourceOrKindArg string) (*meta.RESTMapping, error) { +func (r *ResourceRepo) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, error) { fullySpecifiedGVR, groupResource := schema.ParseResourceArg(resourceOrKindArg) gvk := schema.GroupVersionKind{} - restMapper, err := r.ToRESTMapper() + restMapper, err := r.toRESTMapper() if err != nil { return nil, err } diff --git a/pkg/plugin/template_functions_dynamic.go b/pkg/plugin/template_functions_dynamic.go index a4abbb3..484cd22 100644 --- a/pkg/plugin/template_functions_dynamic.go +++ b/pkg/plugin/template_functions_dynamic.go @@ -19,7 +19,6 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes/scheme" "k8s.io/klog/v2" "k8s.io/kubectl/pkg/cmd/events" @@ -129,23 +128,21 @@ func (r RenderableObject) KubeGetResourcesOwnedOf(resourceOrKind string) (out [] return } klog.V(5).InfoS("called template method KubeGetResourcesOwnedOf", "r", r) - restMapper, _ := r.engine.repo.MappingFor(resourceOrKind) - dynamicInterface, _ := r.engine.repo.DynamicClient() - controllerRevisions, _ := dynamicInterface. - Resource(restMapper.Resource). - Namespace(r.GetNamespace()). - List(context.TODO(), metav1.ListOptions{}) - for _, controllerRevision := range controllerRevisions.Items { - if doesOwnerMatch(r.Unstructured, controllerRevision) { - out = append(out, r.engine.newRenderableObject(controllerRevision.Object)) + objects, err := r.engine.repo.Objects(r.GetNamespace(), []string{resourceOrKind}, "") + if err != nil { + klog.V(2).InfoS("failed to get objects", "r", r, "resourceOrKind", resourceOrKind) + } + for _, object := range objects { + if doesOwnerMatch(r.Unstructured.Object, object) { + out = append(out, r.engine.newRenderableObject(object)) } } return } -func doesOwnerMatch(owner, owned unstructured.Unstructured) bool { - for _, ownerReference := range owner.GetOwnerReferences() { - if ownerReference.UID == owned.GetUID() { +func doesOwnerMatch(owner, owned input.Object) bool { + for _, ownerReference := range owner.Unstructured().GetOwnerReferences() { + if ownerReference.UID == owned.Unstructured().GetUID() { return true } } @@ -400,22 +397,16 @@ func (r RenderableObject) KubeGetUnifiedDiffString(resourceOrKind, namespace, na } func (r RenderableObject) kubeGetUnifiedDiffString(resourceOrKind, namespace, nameA, nameB string) (string, error) { - controllerRevisionMapping, err := r.engine.repo.MappingFor(resourceOrKind) + gvr, err := r.engine.repo.GVRFor(resourceOrKind) if err != nil { klog.V(3).ErrorS(err, "failed to get mapping", "resourceOrKind", resourceOrKind) return "", err } - gvr := controllerRevisionMapping.Resource - dynamicClient, err := r.engine.repo.DynamicClient() - if err != nil { - klog.V(3).ErrorS(err, "failed to get dynamic client") - return "", err - } - aKind, aBytes, aTime, err := getObjectDetailsForDiff(dynamicClient, gvr, namespace, nameA) + aKind, aBytes, aTime, err := r.getObjectDetailsForDiff(gvr, namespace, nameA) if err != nil { return "", err } - bKind, bBytes, bTime, err := getObjectDetailsForDiff(dynamicClient, gvr, namespace, nameB) + bKind, bBytes, bTime, err := r.getObjectDetailsForDiff(gvr, namespace, nameB) if err != nil { return "", err } @@ -431,12 +422,13 @@ func (r RenderableObject) kubeGetUnifiedDiffString(resourceOrKind, namespace, na return difflib.GetUnifiedDiffString(diff) } -func getObjectDetailsForDiff(dynamicClient dynamic.Interface, gvr schema.GroupVersionResource, namespace string, name string) (string, []byte, time.Time, error) { - obj, err := dynamicClient.Resource(gvr).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{}) +func (r RenderableObject) getObjectDetailsForDiff(gvr schema.GroupVersionResource, namespace string, name string) (string, []byte, time.Time, error) { + object, err := r.engine.repo.DynamicObject(gvr, namespace, name) if err != nil { - klog.V(3).ErrorS(err, "failed to query object") + klog.V(2).ErrorS(err, "failed to query object", "gvr", gvr, "namespace", namespace, "name", name) return "", nil, time.Time{}, err } + obj := object.Unstructured() creationTime := obj.GetCreationTimestamp().Time removeFieldsThatCreateDiffNoise(obj) objBytes, err := json.MarshalIndent(obj, "", " ") From ff40fa86b877e189af98aa3ab0aa8df09645eb08 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Fri, 9 Aug 2024 16:45:42 +0100 Subject: [PATCH 07/17] improve minikube helper --- cmd/main_test.go | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/cmd/main_test.go b/cmd/main_test.go index 5301062..1436ef1 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -33,6 +33,7 @@ type cmdTest struct { func (c cmdTest) assert(t *testing.T) { t.Helper() + t.Logf("running cmdTest assert: %s", c) stdout, stderr, err := executeCMD(t, c.args) switch { case c.stdoutRegex == "" && c.stdoutEqual == "": @@ -117,7 +118,7 @@ func TestE2EAgainstVanillaMinikube(t *testing.T) { t.Skip("Skipping e2e test") } if os.Getenv("ASSUME_MINIKUBE_IS_CONFIGURED") != "true" { - defer startMinikube(t, "kubectl-status-e2e")() + defer startMinikube(t)() } defer plugin.SetDurationRound(func(_ interface{}) string { return "1m" })() klog.InitFlags(nil) @@ -188,23 +189,24 @@ func executeCMD(t *testing.T, args []string) (string, string, error) { return stdout.String(), stderr.String(), err } -func startMinikube(t *testing.T, clusterName string) (deleteMinikube func()) { +func startMinikube(t *testing.T) (deleteMinikube func()) { t.Helper() - t.Log("Creating temp folder for minikube.kubeconfig...") + clusterName := t.Name() + t.Logf("Creating temp folder for minikube.kubeconfig for minikube %s ...", clusterName) dir, err := os.MkdirTemp("", clusterName) assert.NoError(t, err) kubeconfig := path.Join(dir, "minikube.kubeconfig") t.Setenv("KUBECONFIG", kubeconfig) - t.Log("Starting Minikube cluster...") + t.Logf("Starting Minikube cluster %s with %s ...", clusterName, kubeconfig) startMinikube := exec.Command("minikube", "start", "-p", clusterName) assert.NoError(t, startMinikube.Run()) return func() { cmd := exec.Command("minikube", "delete", "-p", clusterName) - t.Log("Deleting Minikube cluster...") + t.Logf("Deleting Minikube cluster %s...", clusterName) if err := cmd.Run(); err != nil { t.Log("Error deleting Minikube cluster:", err) } - t.Log("Deleting temp folder of minikube.kubeconfig...") + t.Logf("Deleting temp folder for minikube %s: %s ...", clusterName, dir) if err := os.RemoveAll(dir); err != nil { t.Log("Error deleting temp folder of minikube.kubeconfig:", err) } @@ -216,7 +218,7 @@ func TestE2EDynamicManifests(t *testing.T) { t.Skip("Skipping e2e test") } if os.Getenv("ASSUME_MINIKUBE_IS_CONFIGURED") != "true" { - defer startMinikube(t, "kubectl-status-e2e")() + defer startMinikube(t)() } defer plugin.SetDurationRound(func(_ interface{}) string { return "1m" })() viper.Set("test", true) @@ -241,31 +243,34 @@ func TestE2EDynamicManifests(t *testing.T) { }, } owner, err := clientset.CoreV1().Secrets("default").Create(context.TODO(), owner, metav1.CreateOptions{}) - defer clientset.CoreV1().Secrets("default").Delete(context.TODO(), owner.Name, metav1.DeleteOptions{}) + defer clientset.CoreV1().Secrets("default").Delete(context.TODO(), "owner", metav1.DeleteOptions{}) assert.NoError(t, err) - // Create the owned secret with owner reference - owned := &corev1.Secret{ + uid := owner.GetUID() + t.Logf("owner secret is created, uid is %s", uid) + // Create the child secret with owner reference + child := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "owned", + Name: "child", Namespace: "default", OwnerReferences: []metav1.OwnerReference{ { APIVersion: "v1", Kind: "Secret", Name: "owner", - UID: owner.UID, + UID: uid, }, }, }, } - _, err = clientset.CoreV1().Secrets("default").Create(context.TODO(), owned, metav1.CreateOptions{}) - defer clientset.CoreV1().Secrets("default").Delete(context.TODO(), owned.Name, metav1.DeleteOptions{}) + _, err = clientset.CoreV1().Secrets("default").Create(context.TODO(), child, metav1.CreateOptions{}) + t.Log("child secret is created") + defer clientset.CoreV1().Secrets("default").Delete(context.TODO(), "child", metav1.DeleteOptions{}) assert.NoError(t, err) test := cmdTest{ - args: []string{"secret/owned", "--deep", "--v", "255"}, + args: []string{"secret/child", "--deep", "--v", "7"}, stdoutRegex: `(?ms) -Secret\/owned -n default, created 1m ago by Secret/owner +Secret\/child -n default, created 1m ago by Secret/owner Current: Resource is always ready Known\/recorded manage events: 1m ago Updated by [^ ]+ \(metadata, type\) From a7e7c36bb768c486ea0cc65f6f4c954fdc2d95fd Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Sat, 10 Aug 2024 02:01:18 +0100 Subject: [PATCH 08/17] add better viper handling for test-hacks --- cmd/main.go | 16 +- cmd/main_test.go | 160 ++++++++++++++----- pkg/input/input.go | 23 ++- pkg/plugin/renderable.go | 2 +- pkg/plugin/template_functions_dynamic.go | 11 +- pkg/plugin/template_functions_static.go | 9 ++ pkg/plugin/templates/Service.tmpl | 2 +- tests/artifacts/README.md | 4 +- tests/e2e-artifacts/sts-with-ingress.pod.out | 22 +++ tests/e2e-artifacts/sts-with-ingress.yaml | 46 ++++++ 10 files changed, 230 insertions(+), 65 deletions(-) create mode 100644 tests/e2e-artifacts/sts-with-ingress.pod.out create mode 100644 tests/e2e-artifacts/sts-with-ingress.yaml diff --git a/cmd/main.go b/cmd/main.go index 85983c8..5ef097e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -81,14 +81,17 @@ func RootCmd() *cobra.Command { Long: longCmdMessage, Example: examplesMessage, PreRun: func(cmd *cobra.Command, args []string) { - _ = viper.BindPFlags(cmd.Flags()) + viper.AutomaticEnv() + err := viper.BindPFlags(cmd.Flags()) + if err != nil { + cmd.PrintErr("error binding flags", err) + } }, SilenceUsage: true, Version: version, } initColorCobra(cmd) configFlags := initFlags(cmd) - cobra.OnInitialize(viper.AutomaticEnv) viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) f := cmdutil.NewFactory(configFlags) cmd.RunE = func(cmd *cobra.Command, args []string) error { @@ -99,7 +102,8 @@ func RootCmd() *cobra.Command { }) cmdutil.CheckErr(complete(f)) cmdutil.CheckErr(validate()) - if b, _ := cmd.Flags().GetBool("time-hack-ago"); b { + if b, _ := cmd.Flags().GetBool("test-hack"); b { + viper.Set("test-hack", true) plugin.SetDurationRound(func(_ interface{}) string { return "1m" }) } ioStreams := genericiooptions.IOStreams{In: cmd.InOrStdin(), Out: cmd.OutOrStdout(), ErrOut: cmd.ErrOrStderr()} @@ -151,7 +155,7 @@ func hideNoisyFlags(flags *pflag.FlagSet) { "certificate-authority", "client-certificate", "client-key", "cluster", "context", "insecure-skip-tls-verify", "kubeconfig", "log_backtrace_at", "log_dir", "log_file", "log_file_max_size", "logtostderr", "one_output", "password", "request-timeout", "server", "skip_headers", "skip_log_headers", "stderrthreshold", - "tls-server-name", "token", "user", "username", "vmodule", "time-hack-ago"} + "tls-server-name", "token", "user", "username", "vmodule", "test-hack"} for _, flagName := range flagsToHide { flags.Lookup(flagName).Hidden = true } @@ -201,8 +205,8 @@ func addRenderFlags(flags *pflag.FlagSet) { "Show all available flags.") flags.String("color", "auto", "One of 'auto', 'never' or 'always'.") - flags.Bool("time-hack-ago", false, - "always report 1m for any time duration") + flags.Bool("test-hack", false, + "helper flag for tests, e.g. always report 1m for any time duration, 1.1.1.1 for IPs, etc.") } func isBoolConfigExplicitlySetToTrue(key string) bool { diff --git a/cmd/main_test.go b/cmd/main_test.go index 1436ef1..64d7562 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -7,11 +7,13 @@ import ( "os/exec" "path" "path/filepath" + "regexp" "strings" "testing" "github.com/spf13/viper" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -22,26 +24,45 @@ import ( ) type cmdTest struct { - name string - args []string - stdoutRegex string // Regex - stdoutEqual string // Regex - stderrRegex string // Regex - stderrEqual string // Regex - wantErr string // Contains + name string + args []string + stdoutRegex string // Regex + stdoutRegexPath string // Regex match against file contents under test folder + stdoutEqual string // Exact + stdoutEqualPath string // Exact match with file contents under test folder + stderrRegex string // Regex + stderrEqual string // Exact + wantErr string // Contains } -func (c cmdTest) assert(t *testing.T) { +func nodeNameModifier(stdout string) string { + return string(regexp.MustCompile(`Node/[a-z0-9-]+`).ReplaceAll([]byte(stdout), []byte(`Node/minikube`))) +} +func (c cmdTest) assert(t *testing.T, stdoutModifier func(string) string) { t.Helper() t.Logf("running cmdTest assert: %s", c) stdout, stderr, err := executeCMD(t, c.args) + if stdoutModifier != nil { + stdout = nodeNameModifier(stdout) + } switch { - case c.stdoutRegex == "" && c.stdoutEqual == "": + case c.stdoutRegex == "" && c.stdoutEqual == "" && c.stdoutRegexPath == "" && c.stdoutEqualPath == "": assert.Empty(t, stdout) case c.stdoutRegex != "": assert.Regexp(t, c.stdoutRegex, stdout) case c.stdoutEqual != "": assert.Equal(t, c.stdoutEqual, stdout) + case c.stdoutEqualPath != "": + outFile := path.Join("..", "tests", c.stdoutEqualPath) + out, err := os.ReadFile(outFile) + assert.NoErrorf(t, err, "failed to read test artifact file: %s", outFile) + assert.Equal(t, string(out), stdout) + case c.stdoutRegexPath != "": + outFile := path.Join("..", "tests", c.stdoutRegexPath) + regexBytes, err := os.ReadFile(outFile) + assert.NoErrorf(t, err, "failed to read test artifact file: %s", outFile) + regex := `(?ms)` + string(regexBytes) + assert.Regexp(t, regex, stdout) } switch { case c.stderrRegex == "" && c.stderrEqual == "": @@ -108,19 +129,14 @@ $`, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.assert(t) + tt.assert(t, nil) }) } } func TestE2EAgainstVanillaMinikube(t *testing.T) { - if os.Getenv("RUN_E2E_TESTS") != "true" { - t.Skip("Skipping e2e test") - } - if os.Getenv("ASSUME_MINIKUBE_IS_CONFIGURED") != "true" { - defer startMinikube(t)() - } - defer plugin.SetDurationRound(func(_ interface{}) string { return "1m" })() + e2eMinikubeTest(t) + testHack(t) klog.InitFlags(nil) t.Log("starting tests...") tests := []cmdTest{ @@ -151,27 +167,45 @@ func TestE2EAgainstVanillaMinikube(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.assert(t) + viperTestHack(t) + tt.assert(t, nil) }) } } -func TestAllArtifacts(t *testing.T) { - defer plugin.SetDurationRound(func(_ interface{}) string { return "1m" })() +func testHack(t *testing.T) { + t.Helper() + durationRevert := plugin.SetDurationRound(func(_ interface{}) string { return "1m" }) + t.Cleanup(func() { + durationRevert() + }) +} + +func viperTestHack(t *testing.T) { + t.Helper() + viper.Reset() + viper.Set("test-hack", true) + t.Cleanup(func() { + viper.Reset() + }) + +} + +func TestAllArtifactsLocal(t *testing.T) { t.Setenv("KUBECONFIG", "/dev/null") - viper.Set("test", true) + testHack(t) + viperTestHack(t) artifacts, err := filepath.Glob("../tests/artifacts/*.yaml") assert.NoError(t, err) for _, artifact := range artifacts { - t.Run(strings.Replace(artifact, "../", "", -1), func(t *testing.T) { - outFile := strings.Replace(artifact, ".yaml", ".out", -1) - out, err := os.ReadFile(outFile) - assert.NoError(t, err) + name := strings.Replace(artifact, "../tests/", "", 1) + name = strings.Replace(name, ".yaml", "", 1) + t.Run(name, func(t *testing.T) { test := cmdTest{ - args: []string{"-f", artifact, "--local", "--shallow", "--v", "255"}, - stdoutEqual: string(out), + args: []string{"-f", artifact, "--local", "--shallow", "--v", "255"}, + stdoutEqualPath: name + ".out", } - test.assert(t) // to update the out files check /tests/artifacts/README.md + test.assert(t, nil) // to update the out files check /tests/artifacts/README.md }) } } @@ -189,18 +223,18 @@ func executeCMD(t *testing.T, args []string) (string, string, error) { return stdout.String(), stderr.String(), err } -func startMinikube(t *testing.T) (deleteMinikube func()) { +func startMinikube(t *testing.T) { t.Helper() clusterName := t.Name() t.Logf("Creating temp folder for minikube.kubeconfig for minikube %s ...", clusterName) dir, err := os.MkdirTemp("", clusterName) - assert.NoError(t, err) + require.NoError(t, err) kubeconfig := path.Join(dir, "minikube.kubeconfig") t.Setenv("KUBECONFIG", kubeconfig) t.Logf("Starting Minikube cluster %s with %s ...", clusterName, kubeconfig) startMinikube := exec.Command("minikube", "start", "-p", clusterName) - assert.NoError(t, startMinikube.Run()) - return func() { + require.NoError(t, startMinikube.Run()) + t.Cleanup(func() { cmd := exec.Command("minikube", "delete", "-p", clusterName) t.Logf("Deleting Minikube cluster %s...", clusterName) if err := cmd.Run(); err != nil { @@ -210,18 +244,23 @@ func startMinikube(t *testing.T) (deleteMinikube func()) { if err := os.RemoveAll(dir); err != nil { t.Log("Error deleting temp folder of minikube.kubeconfig:", err) } - } + }) } - -func TestE2EDynamicManifests(t *testing.T) { +func e2eMinikubeTest(t *testing.T) { + t.Helper() if os.Getenv("RUN_E2E_TESTS") != "true" { - t.Skip("Skipping e2e test") + t.Skip("Skipping e2e test as RUN_E2E_TESTS is not set to true") } - if os.Getenv("ASSUME_MINIKUBE_IS_CONFIGURED") != "true" { - defer startMinikube(t)() + if os.Getenv("ASSUME_MINIKUBE_IS_CONFIGURED") == "true" { + t.Logf("assuming current kubeconfig context is pointng a minikube to run e2e tests") + } else { + startMinikube(t) } - defer plugin.SetDurationRound(func(_ interface{}) string { return "1m" })() - viper.Set("test", true) +} + +func TestE2EDynamicManifests(t *testing.T) { + e2eMinikubeTest(t) + testHack(t) kubeconfigPath := os.Getenv("KUBECONFIG") if kubeconfigPath == "" { homeDir := os.Getenv("HOME") @@ -236,6 +275,7 @@ func TestE2EDynamicManifests(t *testing.T) { t.Fatal(err) } t.Run("owners should be included with deep", func(t *testing.T) { + viperTestHack(t) owner := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "owner", @@ -244,7 +284,7 @@ func TestE2EDynamicManifests(t *testing.T) { } owner, err := clientset.CoreV1().Secrets("default").Create(context.TODO(), owner, metav1.CreateOptions{}) defer clientset.CoreV1().Secrets("default").Delete(context.TODO(), "owner", metav1.DeleteOptions{}) - assert.NoError(t, err) + require.NoError(t, err) uid := owner.GetUID() t.Logf("owner secret is created, uid is %s", uid) // Create the child secret with owner reference @@ -265,7 +305,7 @@ func TestE2EDynamicManifests(t *testing.T) { _, err = clientset.CoreV1().Secrets("default").Create(context.TODO(), child, metav1.CreateOptions{}) t.Log("child secret is created") defer clientset.CoreV1().Secrets("default").Delete(context.TODO(), "child", metav1.DeleteOptions{}) - assert.NoError(t, err) + require.NoError(t, err) test := cmdTest{ args: []string{"secret/child", "--deep", "--v", "7"}, @@ -281,6 +321,40 @@ Secret\/child -n default, created 1m ago by Secret/owner 1m ago Updated by [^ ]+ \(type\) `, } - test.assert(t) // to update the out files check /tests/artifacts/README.md + test.assert(t, nil) // to update the out files check /tests/artifacts/README.md }) + t.Run("sts-with-ingress", func(t *testing.T) { + viperTestHack(t) + // using sts here as the pod name is predictable in that case, not true for deployments and ds + applyManifest(t, "e2e-artifacts/sts-with-ingress.yaml") + waitFor(t, "sts/sts-with-ingress", "jsonpath={.status.readyReplicas}=1") + cmdTest{ + args: []string{"pod/sts-with-ingress-0", "--include-events=false", "--v", "5"}, + stdoutEqualPath: "e2e-artifacts/sts-with-ingress.pod.out", + }.assert(t, nodeNameModifier) + }) +} + +func applyManifest(t *testing.T, filepath string) { + t.Helper() + filepath = path.Join("..", "tests", filepath) + cmd := exec.Command("kubectl", "apply", "-f", filepath) + output, err := cmd.CombinedOutput() + t.Cleanup(func() { + t.Logf("deleting manifest %s", filepath) + cmd := exec.Command("kubectl", "delete", "-f", filepath) + output, err := cmd.CombinedOutput() + assert.NoError(t, err) + t.Logf("manifest deleted %s: %s", filepath, string(output)) + }) + require.NoError(t, err) + t.Logf("applied manifest %s: %s", filepath, string(output)) +} + +func waitFor(t *testing.T, resource, forParam string) { + t.Helper() + cmd := exec.Command("kubectl", "wait", "--for", forParam, resource) + output, err := cmd.CombinedOutput() + t.Logf("wait result for %s: %s", resource, string(output)) + require.NoError(t, err) } diff --git a/pkg/input/input.go b/pkg/input/input.go index 606be83..f57508f 100644 --- a/pkg/input/input.go +++ b/pkg/input/input.go @@ -6,6 +6,8 @@ import ( "sort" "github.com/spf13/viper" + corev1 "k8s.io/api/core/v1" + netv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -14,6 +16,9 @@ import ( "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/klog/v2" + "k8s.io/kubectl/pkg/cmd/events" "k8s.io/kubectl/pkg/cmd/util" ) @@ -108,8 +113,15 @@ func (r *ResourceRepo) Objects(namespace string, args []string, labelSelector st return unstructuredObjects, err } -func (r *ResourceRepo) toRESTMapper() (meta.RESTMapper, error) { - return r.f.ToRESTMapper() +func (r *ResourceRepo) ObjectEvents(u *unstructured.Unstructured) (*corev1.EventList, error) { + clientSet, _ := r.KubernetesClientSet() + eventList, err := clientSet.CoreV1().Events(u.GetNamespace()).Search(scheme.Scheme, u) + if err != nil { + klog.V(3).ErrorS(err, "error getting events", "r", r) + return nil, err + } + sort.Sort(events.SortableEvents(eventList.Items)) + return eventList, nil } func (r *ResourceRepo) KubernetesClientSet() (*kubernetes.Clientset, error) { @@ -175,7 +187,7 @@ func (r *ResourceRepo) GVRFor(resourceOrKindArg string) (schema.GroupVersionReso func (r *ResourceRepo) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, error) { fullySpecifiedGVR, groupResource := schema.ParseResourceArg(resourceOrKindArg) gvk := schema.GroupVersionKind{} - restMapper, err := r.toRESTMapper() + restMapper, err := r.f.ToRESTMapper() if err != nil { return nil, err } @@ -217,3 +229,8 @@ func (r *ResourceRepo) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, return mapping, nil } + +func (r *ResourceRepo) Ingresses(namespace string) (*netv1.IngressList, error) { + clientSet, _ := r.KubernetesClientSet() + return clientSet.NetworkingV1().Ingresses(namespace).List(context.TODO(), metav1.ListOptions{}) +} diff --git a/pkg/plugin/renderable.go b/pkg/plugin/renderable.go index 0c1dbea..ed6f07a 100644 --- a/pkg/plugin/renderable.go +++ b/pkg/plugin/renderable.go @@ -141,7 +141,7 @@ func (r RenderableObject) renderTemplate(templateName string, data interface{}) func (r RenderableObject) executeTemplate(wr io.Writer, name string, data any) error { target, ok := data.(RenderableObject) - if ok && target.Kind() == name && renderedUIDs.checkAdd(target.GetUID()) && !viper.GetBool("watching") && !viper.GetBool("test") { + if ok && target.Kind() == name && renderedUIDs.checkAdd(target.GetUID()) && !viper.GetBool("watching") && !viper.GetBool("test-hack") { klog.V(3).InfoS("skip rendering of the RenderableObject as its already rendered", "r", r, "templateName", name) _, _ = color.New(color.FgWhite).Fprintf(wr, "%s is already printed", target.String()) diff --git a/pkg/plugin/template_functions_dynamic.go b/pkg/plugin/template_functions_dynamic.go index 484cd22..3824fb1 100644 --- a/pkg/plugin/template_functions_dynamic.go +++ b/pkg/plugin/template_functions_dynamic.go @@ -6,7 +6,6 @@ import ( "context" "encoding/json" "fmt" - "sort" "strings" "time" @@ -19,9 +18,7 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/klog/v2" - "k8s.io/kubectl/pkg/cmd/events" "k8s.io/kubectl/pkg/polymorphichelpers" "github.com/bergerx/kubectl-status/pkg/input" @@ -108,13 +105,11 @@ func (r RenderableObject) KubeGetEvents() RenderableObject { return nr } klog.V(5).InfoS("called KubeGetEvents", "r", r) - clientSet, _ := r.engine.repo.KubernetesClientSet() - eventList, err := clientSet.CoreV1().Events(r.GetNamespace()).Search(scheme.Scheme, &r) + eventList, err := r.engine.repo.ObjectEvents(&r.Unstructured) if err != nil { klog.V(3).ErrorS(err, "error getting events", "r", r) return nr } - sort.Sort(events.SortableEvents(eventList.Items)) unstructuredEvents, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&eventList) nr.Object = unstructuredEvents return nr @@ -191,9 +186,7 @@ func (r RenderableObject) KubeGetIngressesMatchingService(namespace, svcName str } klog.V(5).InfoS("called KubeGetIngressesMatchingService", "r", r, "namespace", namespace, "svcName", svcName) - clientSet, _ := r.engine.repo.KubernetesClientSet() - // The old v1beta1 Ingress which will no longer served as of v1.22. Not implementing it. - ingresses, err := clientSet.NetworkingV1().Ingresses(namespace).List(context.TODO(), metav1.ListOptions{}) + ingresses, err := r.engine.repo.Ingresses(namespace) if err != nil { klog.V(3).ErrorS(err, "error listing ingresses", "r", r, "namespace", namespace) return diff --git a/pkg/plugin/template_functions_static.go b/pkg/plugin/template_functions_static.go index 148209e..2ad85b9 100644 --- a/pkg/plugin/template_functions_static.go +++ b/pkg/plugin/template_functions_static.go @@ -13,6 +13,7 @@ import ( "github.com/fatih/color" "github.com/go-sprout/sprout" "github.com/spf13/cast" + "github.com/spf13/viper" resource2 "k8s.io/apimachinery/pkg/api/resource" "k8s.io/klog/v2" ) @@ -60,9 +61,17 @@ func funcMap() template.FuncMap { "addFloat64": addFloat64, "subFloat64": subFloat64, "divFloat64": divFloat64, + "ip": ip, } } +func ip(ip string) string { + if viper.GetBool("test-hack") { + return "1.1.1.1" + } + return ip +} + func addFloat64(i ...interface{}) float64 { var a float64 = 0 for _, b := range i { diff --git a/pkg/plugin/templates/Service.tmpl b/pkg/plugin/templates/Service.tmpl index 71ab3b4..5f3e39f 100644 --- a/pkg/plugin/templates/Service.tmpl +++ b/pkg/plugin/templates/Service.tmpl @@ -60,7 +60,7 @@ * address: either Endpoints.subsets.addresses[N] or Endpoints.subsets.notReadyAddresses * ports: Endpoints.subsets.ports */ -}} - {{- $ip := .address.ip }} + {{- $ip := .address.ip | ip }} {{- $hasTargetRef := not (not .address.targetRef) }} {{- if .address.targetRef }} {{- .address.targetRef.kind | bold }}/{{ .address.targetRef.name }} diff --git a/tests/artifacts/README.md b/tests/artifacts/README.md index e879fde..17ef37d 100644 --- a/tests/artifacts/README.md +++ b/tests/artifacts/README.md @@ -11,7 +11,7 @@ cd ../.. for yaml in ./tests/artifacts/*.yaml; do out=$(echo ${yaml} | sed 's/.yaml/.out/') echo "${yaml} --> ${out}" - go run ./cmd --time-hack-ago -f ${yaml} --local --shallow > ${out} + go run ./cmd --test-hack -f ${yaml} --local --shallow > ${out} done ``` @@ -25,7 +25,7 @@ file="" # filename for the new artifact file to be stored, e.g. node-and-servic cd ../.. kubectl get -o yaml ${cmd} > tests/artifacts/${file}.yaml -go run ./cmd --time-hack-ago ${cmd} --shallow > tests/artifacts/${file}.out +go run ./cmd --test-hack ${cmd} --shallow > tests/artifacts/${file}.out make test git add tests/artifacts/${file}.yaml tests/artifacts/${file}.out ``` diff --git a/tests/e2e-artifacts/sts-with-ingress.pod.out b/tests/e2e-artifacts/sts-with-ingress.pod.out new file mode 100644 index 0000000..0821365 --- /dev/null +++ b/tests/e2e-artifacts/sts-with-ingress.pod.out @@ -0,0 +1,22 @@ + +Pod/sts-with-ingress-0 -n default, created 1m ago by StatefulSet/sts-with-ingress Running BestEffort + Current: Pod is Ready + Managed by sts-with-ingress application + PodScheduled -> Initialized -> ContainersReady -> Ready for 1m + Containers: + sts-with-ingress (registry.k8s.io/pause:3.9) Running for 1m and Ready + Known/recorded manage events: + 1m ago Updated by kube-controller-manager (metadata, spec) + 1m ago Updated by kubelet (status) + Services matching this pod: + Service/sts-with-ingress -n default, created 1m ago, last endpoint change was 1m ago + Current: Service is ready + Ready: Pod/sts-with-ingress-0 -n default on Node/minikube, 1.1.1.1:80/TCP + Known/recorded manage events: + 1m ago Updated by kubectl-client-side-apply (metadata, spec) + Ingresses matching this Service: + Ingress/sts-with-ingress -n default, created 1m ago, gen:1 + Current: Resource is current + Service/sts-with-ingress:80 has 1 endpoints. + Known/recorded manage events: + 1m ago Updated by kubectl-client-side-apply (metadata, spec) diff --git a/tests/e2e-artifacts/sts-with-ingress.yaml b/tests/e2e-artifacts/sts-with-ingress.yaml new file mode 100644 index 0000000..095517e --- /dev/null +++ b/tests/e2e-artifacts/sts-with-ingress.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: sts-with-ingress +spec: + replicas: 1 + selector: + matchLabels: + app: sts-with-ingress + template: + metadata: + labels: + app: sts-with-ingress + spec: + containers: + - name: sts-with-ingress + image: registry.k8s.io/pause:3.9 +--- +apiVersion: v1 +kind: Service +metadata: + name: sts-with-ingress +spec: + selector: + app: sts-with-ingress + ports: + - protocol: TCP + port: 80 + targetPort: 80 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: sts-with-ingress +spec: + rules: + - host: sts-with-ingress.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: sts-with-ingress + port: + number: 80 From 16c6019ed3df04b86e7e019e517c1b11192c04a6 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Sat, 10 Aug 2024 15:17:28 +0100 Subject: [PATCH 09/17] unexport input.KubernetesClientSet --- pkg/input/input.go | 109 +++++++++++++++++++++-- pkg/plugin/template_functions_dynamic.go | 54 ++--------- 2 files changed, 107 insertions(+), 56 deletions(-) diff --git a/pkg/input/input.go b/pkg/input/input.go index f57508f..1b8af48 100644 --- a/pkg/input/input.go +++ b/pkg/input/input.go @@ -11,8 +11,10 @@ import ( "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/json" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" @@ -22,11 +24,7 @@ import ( "k8s.io/kubectl/pkg/cmd/util" ) -type ResourceRepo struct { - f util.Factory - client dynamic.Interface -} - +// Object is the JSON compatible map[string]interface{} mostly used through unstructured.Unstructured. type Object map[string]interface{} func (u Object) creationTimestamp() string { @@ -61,6 +59,13 @@ func NewResourceRepo(factory util.Factory) ResourceRepo { } } +type ResourceRepo struct { + f util.Factory + + client dynamic.Interface + clientSet *kubernetes.Clientset +} + func (r *ResourceRepo) newBaseBuilder() *resource.Builder { builder := r.f.NewBuilder(). NamespaceParam(viper.GetString("namespace")). @@ -114,7 +119,11 @@ func (r *ResourceRepo) Objects(namespace string, args []string, labelSelector st } func (r *ResourceRepo) ObjectEvents(u *unstructured.Unstructured) (*corev1.EventList, error) { - clientSet, _ := r.KubernetesClientSet() + clientSet, err := r.kubernetesClientSet() + if err != nil { + klog.V(3).ErrorS(err, "error getting events", "r", r) + return nil, err + } eventList, err := clientSet.CoreV1().Events(u.GetNamespace()).Search(scheme.Scheme, u) if err != nil { klog.V(3).ErrorS(err, "error getting events", "r", r) @@ -124,8 +133,15 @@ func (r *ResourceRepo) ObjectEvents(u *unstructured.Unstructured) (*corev1.Event return eventList, nil } -func (r *ResourceRepo) KubernetesClientSet() (*kubernetes.Clientset, error) { - return r.f.KubernetesClientSet() +func (r *ResourceRepo) kubernetesClientSet() (*kubernetes.Clientset, error) { + if r.clientSet == nil { + var err error + r.clientSet, err = r.f.KubernetesClientSet() + if err != nil { + return nil, err + } + } + return r.clientSet, nil } func (r *ResourceRepo) dynamicClient() (dynamic.Interface, error) { @@ -231,6 +247,81 @@ func (r *ResourceRepo) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, } func (r *ResourceRepo) Ingresses(namespace string) (*netv1.IngressList, error) { - clientSet, _ := r.KubernetesClientSet() + clientSet, err := r.kubernetesClientSet() + if err != nil { + return nil, err + } return clientSet.NetworkingV1().Ingresses(namespace).List(context.TODO(), metav1.ListOptions{}) } + +func (r *ResourceRepo) Services(namespace string) (*corev1.ServiceList, error) { + clientSet, err := r.kubernetesClientSet() + if err != nil { + return nil, err + } + return clientSet.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{}) +} + +func (r *ResourceRepo) Service(namespace, name string) (*corev1.Service, error) { + clientSet, err := r.kubernetesClientSet() + if err != nil { + return nil, err + } + return clientSet.CoreV1().Services(namespace).Get(context.TODO(), name, metav1.GetOptions{}) +} + +func (r *ResourceRepo) Endpoints(namespace string) (*corev1.EndpointsList, error) { + clientSet, err := r.kubernetesClientSet() + if err != nil { + return nil, err + } + return clientSet.CoreV1().Endpoints(namespace).List(context.TODO(), metav1.ListOptions{}) +} + +// KubeGetNodeStatsSummary returns this structure +// > kubectl get --raw /api/v1/nodes/{nodeName}/proxy/stats/summary +// The endpoint that this function uses will be disabled soon: https://github.com/kubernetes/kubernetes/issues/68522 +func (r *ResourceRepo) KubeGetNodeStatsSummary(nodeName string) (Object, error) { + clientSet, err := r.kubernetesClientSet() + if err != nil { + return nil, err + } + getBytes, err := clientSet.CoreV1().RESTClient().Get(). + Resource("nodes"). + SubResource("proxy"). + Name(nodeName). + Suffix("stats/summary"). + DoRaw(context.TODO()) + if err != nil { + return nil, err + } + nodeStatsSummary := make(Object) + err = json.Unmarshal(getBytes, &nodeStatsSummary) + return nodeStatsSummary, err +} + +func (r *ResourceRepo) NonTerminatedPodsOnTheNode(nodeName string) (Objects, error) { + clientSet, _ := r.kubernetesClientSet() + fieldSelector, err := fields.ParseSelector("spec.nodeName=" + nodeName + + ",status.phase!=" + string(corev1.PodSucceeded) + + ",status.phase!=" + string(corev1.PodFailed)) + if err != nil { + klog.V(3).ErrorS(err, "Failed creating fieldSelector for non-terminated Pods on Node", + "r", r, "nodeName", nodeName) + return nil, err + } + nodeNonTerminatedPodsList, err := clientSet.CoreV1(). + Pods(""). // Search in all namespaces + List(context.TODO(), metav1.ListOptions{FieldSelector: fieldSelector.String()}) + if err != nil { + klog.V(3).ErrorS(err, "Failed getting non-terminated Pods for Node", + "r", r, "nodeName", nodeName) + return nil, err + } + pods := Objects{} + for _, pod := range nodeNonTerminatedPodsList.Items { + unstructuredPod, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&pod) + pods = append(pods, unstructuredPod) + } + return pods, nil +} diff --git a/pkg/plugin/template_functions_dynamic.go b/pkg/plugin/template_functions_dynamic.go index 3824fb1..56a7881 100644 --- a/pkg/plugin/template_functions_dynamic.go +++ b/pkg/plugin/template_functions_dynamic.go @@ -3,7 +3,6 @@ package plugin import ( - "context" "encoding/json" "fmt" "strings" @@ -15,7 +14,6 @@ import ( netv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/klog/v2" @@ -225,8 +223,7 @@ func (r RenderableObject) KubeGetServicesMatchingLabels(namespace string, labels castedLabels[k] = v.(string) } klog.V(5).InfoS("casted labels values into string", "r", r, "castedLabels", castedLabels) - clientSet, _ := r.engine.repo.KubernetesClientSet() - svcs, err := clientSet.CoreV1().Services(r.Namespace()).List(context.TODO(), metav1.ListOptions{}) + svcs, err := r.engine.repo.Services(r.Namespace()) if err != nil { klog.V(3).ErrorS(err, "error listing services", "r", r, "namespace", namespace) return out @@ -246,8 +243,7 @@ func (r RenderableObject) KubeGetServicesMatchingPod(namespace, podName string) return } klog.V(5).InfoS("called KubeGetServicesMatchingPod", "r", r, "namespace", namespace, "podName", podName) - clientSet, _ := r.engine.repo.KubernetesClientSet() - endpoints, err := clientSet.CoreV1().Endpoints(r.Namespace()).List(context.TODO(), metav1.ListOptions{}) + endpoints, err := r.engine.repo.Endpoints(r.Namespace()) if err != nil { klog.V(3).ErrorS(err, "error listing endpoints", "r", r, "namespace", namespace) return @@ -262,7 +258,7 @@ func (r RenderableObject) KubeGetServicesMatchingPod(namespace, podName string) } } if matched { - svc, err := clientSet.CoreV1().Services(r.Namespace()).Get(context.TODO(), ep.Name, metav1.GetOptions{}) + svc, err := r.engine.repo.Service(r.Namespace(), ep.Name) if err != nil { klog.V(3).ErrorS(err, "error getting matching service", "r", r, "namespace", namespace, "name", ep.Name) continue @@ -300,7 +296,7 @@ func (r RenderableObject) KubeGetNodeStatsSummary(nodeName string) map[string]in return nil } klog.V(5).InfoS("called KubeGetNodeStatsSummary", "r", r, "node", nodeName) - nodeStatsSummary, err := r.kubeGetNodeStatsSummary(nodeName) + nodeStatsSummary, err := r.engine.repo.KubeGetNodeStatsSummary(nodeName) if err != nil { klog.V(3).ErrorS(err, "failed to get node stats summary", "r", r, "node", nodeName) return nil @@ -308,30 +304,6 @@ func (r RenderableObject) KubeGetNodeStatsSummary(nodeName string) map[string]in return nodeStatsSummary } -// kubeGetNodeStatsSummary returns this structure -// -// > kubectl get --raw /api/v1/nodes/{nodeName}/proxy/stats/summary -// -// The endpoint that this function uses will be disabled soon: https://github.com/kubernetes/kubernetes/issues/68522 -func (r RenderableObject) kubeGetNodeStatsSummary(nodeName string) (map[string]interface{}, error) { - clientSet, err := r.engine.repo.KubernetesClientSet() - if err != nil { - return nil, err - } - getBytes, err := clientSet.CoreV1().RESTClient().Get(). - Resource("nodes"). - SubResource("proxy"). - Name(nodeName). - Suffix("stats/summary"). - DoRaw(context.TODO()) - if err != nil { - return nil, err - } - nodeStatsSummary := make(map[string]interface{}) - err = json.Unmarshal(getBytes, &nodeStatsSummary) - return nodeStatsSummary, err -} - // KubeGetNonTerminatedPodsOnNode returns details of all pods which are not in terminal status func (r RenderableObject) KubeGetNonTerminatedPodsOnNode(nodeName string) (podList []RenderableObject) { if viper.GetBool("shallow") { @@ -347,26 +319,14 @@ func (r RenderableObject) KubeGetNonTerminatedPodsOnNode(nodeName string) (podLi } func (r RenderableObject) kubeGetNonTerminatedPodsOnTheNode(nodeName string) (podList []RenderableObject, err error) { - clientSet, _ := r.engine.repo.KubernetesClientSet() - fieldSelector, err := fields.ParseSelector("spec.nodeName=" + nodeName + - ",status.phase!=" + string(corev1.PodSucceeded) + - ",status.phase!=" + string(corev1.PodFailed)) - if err != nil { - klog.V(3).ErrorS(err, "Failed creating fieldSelector for non-terminated Pods on Node", - "r", r, "nodeName", nodeName) - return - } - nodeNonTerminatedPodsList, err := clientSet.CoreV1(). - Pods(""). // Search in all namespaces - List(context.TODO(), metav1.ListOptions{FieldSelector: fieldSelector.String()}) + pods, err := r.engine.repo.NonTerminatedPodsOnTheNode(nodeName) if err != nil { klog.V(3).ErrorS(err, "Failed getting non-terminated Pods for Node", "r", r, "nodeName", nodeName) return } - for _, pod := range nodeNonTerminatedPodsList.Items { - unstructuredPod, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&pod) - nr := r.engine.newRenderableObject(unstructuredPod) + for _, pod := range pods { + nr := r.engine.newRenderableObject(pod) podList = append(podList, nr) } return podList, nil From 47aa51609c59b3021b36a097d14309ed56ed3b90 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Sun, 11 Aug 2024 00:12:00 +0100 Subject: [PATCH 10/17] add Owners and FirstObject to input --- pkg/input/input.go | 65 ++++++++++++++ pkg/plugin/template_functions_dynamic.go | 109 ++++------------------- 2 files changed, 80 insertions(+), 94 deletions(-) diff --git a/pkg/input/input.go b/pkg/input/input.go index 1b8af48..1b14345 100644 --- a/pkg/input/input.go +++ b/pkg/input/input.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "sort" + "strings" "github.com/spf13/viper" corev1 "k8s.io/api/core/v1" @@ -118,6 +119,70 @@ func (r *ResourceRepo) Objects(namespace string, args []string, labelSelector st return unstructuredObjects, err } +func (r *ResourceRepo) Owners(obj Object) (out Objects, err error) { + uobj := obj.Unstructured() + namespace := uobj.GetNamespace() + owners := uobj.GetOwnerReferences() + if len(owners) == 0 { + klog.V(4).InfoS("KubeGetOwners Object has no owners", "r", r) + return nil, fmt.Errorf("Object has no owners: %s", obj) + } + for _, owner := range owners { + gv, err := schema.ParseGroupVersion(owner.APIVersion) + var kindVersionGroup string + if err != nil { + klog.V(3).InfoS("repo.Owners failed parsing apiVersion", "apiVersion", owner.APIVersion) + kindVersionGroup = owner.Kind + object, err := r.FirstObject(namespace, []string{kindVersionGroup}, owner.Name) + if err != nil { + klog.V(3).InfoS("repo.Owners failed to get owner using Kind", "apiVersion", owner.APIVersion) + continue + } + out = append(out, object) + continue + } + if gv.Group == "" && gv.Version != "v1" { + kindVersionGroup = fmt.Sprintf("%s.%s", owner.Kind, gv.Version) + klog.V(5).InfoS("repo.Owners", "kindVersionGroup", kindVersionGroup, "gv", gv) + ownerWithVersion, err := r.FirstObject(namespace, []string{kindVersionGroup, owner.Name}, "") + if err != nil { + klog.V(3).InfoS("repo.Owners failed to get owner using kind+version", "apiVersion", owner.APIVersion) + continue + } + if ownerWithVersion == nil { + // it's likely the ownerReference.apiVersion field doesn't have the group prefix, so we'll try without the version + ownerWithVersion, err = r.FirstObject(namespace, []string{owner.Kind, owner.Name}, "") + if err != nil { + klog.V(3).InfoS("repo.Owners failed to get owner using kind+version", "apiVersion", owner.APIVersion) + continue + } + } + out = append(out, ownerWithVersion) + continue + } + kindVersionGroup = fmt.Sprintf("%s.%s.%s", owner.Kind, gv.Version, gv.Group) + klog.V(5).InfoS("repo.Owners", "kindVersionGroup", kindVersionGroup) + object, err := r.FirstObject(namespace, []string{kindVersionGroup, owner.Name}, "") + if err != nil { + klog.V(3).InfoS("repo.Owners failed to get owner using kind+version+group", "apiVersion", owner.APIVersion) + continue + } + out = append(out, object) + } + return out, nil +} + +func (r *ResourceRepo) FirstObject(namespace string, args []string, labelSelector string) (Object, error) { + objects, err := r.Objects(namespace, args, labelSelector) + if err != nil { + return nil, err + } + if len(objects) == 0 { + return nil, fmt.Errorf("no objects found in namespace %s for '%s'", namespace, strings.Join(args, " ")) + } + return objects[0], err +} + func (r *ResourceRepo) ObjectEvents(u *unstructured.Unstructured) (*corev1.EventList, error) { clientSet, err := r.kubernetesClientSet() if err != nil { diff --git a/pkg/plugin/template_functions_dynamic.go b/pkg/plugin/template_functions_dynamic.go index 56a7881..13182bf 100644 --- a/pkg/plugin/template_functions_dynamic.go +++ b/pkg/plugin/template_functions_dynamic.go @@ -31,15 +31,12 @@ func (r RenderableObject) KubeGet(namespace string, args ...string) (out []Rende if err != nil { klog.V(3).ErrorS(err, "ignoring resource error", "r", r, "namespace", namespace, "args", args) } - return r.getCreationTimestampSortedRenderableObjects(objects) + return r.objectsToRenderableObjects(objects) } -func (r RenderableObject) getCreationTimestampSortedRenderableObjects(objects input.Objects) []RenderableObject { - var out []RenderableObject +func (r RenderableObject) objectsToRenderableObjects(objects input.Objects) (out []RenderableObject) { for _, obj := range objects { - unstructuredObj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) - nr := r.engine.newRenderableObject(unstructuredObj) - klog.V(5).InfoS("KubeGet matched object", "object", nr) + nr := r.engine.newRenderableObject(obj) out = append(out, nr) } return out @@ -47,28 +44,16 @@ func (r RenderableObject) getCreationTimestampSortedRenderableObjects(objects in // KubeGetFirst returns a new RenderableObject with a nil Object when no object found. func (r RenderableObject) KubeGetFirst(namespace string, args ...string) RenderableObject { - return r.engine.KubeGetFirst(namespace, args...) -} - -// KubeGetFirst returns a new RenderableObject with a nil Object when no object found. -func (e *renderEngine) KubeGetFirst(namespace string, args ...string) RenderableObject { - nr := e.newRenderableObject(nil) + nr := r.engine.newRenderableObject(nil) if viper.GetBool("shallow") { return nr } klog.V(5).InfoS("called template method KubeGetFirst", "namespace", namespace, "args", args) - objects, err := e.repo.Objects(namespace, args, "") + var err error + nr.Object, err = r.engine.repo.FirstObject(namespace, args, "") if err != nil { - klog.V(3).ErrorS(err, "getResourceQueryInfos failed", - "namespace", namespace, "args", args) - return nr - } - if len(objects) >= 1 { - first := objects[0] - nr.Object = first - } else { - klog.V(3).InfoS("KubeGetFirst returning empty", + klog.V(3).ErrorS(err, "KubeGetFirst failed", "namespace", namespace, "args", args) } return nr @@ -94,7 +79,7 @@ func (r RenderableObject) KubeGetByLabelsMap(namespace, resourceType string, lab "r", r, "namespace", namespace, "labels", labels) return } - return r.getCreationTimestampSortedRenderableObjects(unstructuredObjects) + return r.objectsToRenderableObjects(unstructuredObjects) } func (r RenderableObject) KubeGetEvents() RenderableObject { @@ -109,73 +94,21 @@ func (r RenderableObject) KubeGetEvents() RenderableObject { return nr } unstructuredEvents, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&eventList) - nr.Object = unstructuredEvents + nr.Object = unstructuredEvents // TODO: convert input.Events to return Objects return nr } -// KubeGetResourcesOwnedOf is meant to be called from templates. -// It returns a RenderableObject list for all resources which have provided kind or resource type with the current -// object listed in the ownerReferences. -func (r RenderableObject) KubeGetResourcesOwnedOf(resourceOrKind string) (out []RenderableObject) { - if viper.GetBool("shallow") { - return - } - klog.V(5).InfoS("called template method KubeGetResourcesOwnedOf", "r", r) - objects, err := r.engine.repo.Objects(r.GetNamespace(), []string{resourceOrKind}, "") - if err != nil { - klog.V(2).InfoS("failed to get objects", "r", r, "resourceOrKind", resourceOrKind) - } - for _, object := range objects { - if doesOwnerMatch(r.Unstructured.Object, object) { - out = append(out, r.engine.newRenderableObject(object)) - } - } - return -} - -func doesOwnerMatch(owner, owned input.Object) bool { - for _, ownerReference := range owner.Unstructured().GetOwnerReferences() { - if ownerReference.UID == owned.Unstructured().GetUID() { - return true - } - } - return false -} - // KubeGetOwners returns the list of objects which are listed in the Owner references of an object. func (r RenderableObject) KubeGetOwners() (out []RenderableObject) { if viper.GetBool("shallow") { return } klog.V(5).InfoS("KubeGetOwners called KubeGetOwners", "r", r) - owners := r.GetOwnerReferences() - if len(owners) == 0 { - klog.V(4).InfoS("KubeGetOwners Object has no owners", "r", r) - return - } - for _, owner := range owners { - gv, err := schema.ParseGroupVersion(owner.APIVersion) - var kindVersionGroup string - if err != nil { - klog.V(3).InfoS("KubeGetOwners failed parsing apiVersion", "apiVersion", owner.APIVersion) - kindVersionGroup = owner.Kind - out = append(out, r.KubeGetFirst(r.Namespace(), kindVersionGroup, owner.Name)) - } else if gv.Group == "" && gv.Version != "v1" { - kindVersionGroup = fmt.Sprintf("%s.%s", owner.Kind, gv.Version) - klog.V(5).InfoS("KubeGetOwners", "kindVersionGroup", kindVersionGroup, "gv", gv) - ownerWithVersion := r.KubeGetFirst(r.Namespace(), kindVersionGroup, owner.Name) - if ownerWithVersion.Object == nil { - // its likely the ownerReference.apiVersion field doesn't have the group prefix, so we'll try without the version - ownerWithVersion = r.KubeGetFirst(r.Namespace(), owner.Kind, owner.Name) - } - out = append(out, ownerWithVersion) - } else { - kindVersionGroup = fmt.Sprintf("%s.%s.%s", owner.Kind, gv.Version, gv.Group) - klog.V(5).InfoS("KubeGetOwners", "kindVersionGroup", kindVersionGroup) - out = append(out, r.KubeGetFirst(r.Namespace(), kindVersionGroup, owner.Name)) - } + owners, err := r.engine.repo.Owners(r.Object) + if err != nil { + klog.V(3).ErrorS(err, "error getting owners", "r", r) } - return out + return r.objectsToRenderableObjects(owners) } func (r RenderableObject) KubeGetIngressesMatchingService(namespace, svcName string) (out []RenderableObject) { @@ -237,6 +170,7 @@ func (r RenderableObject) KubeGetServicesMatchingLabels(namespace string, labels } return out } + func (r RenderableObject) KubeGetServicesMatchingPod(namespace, podName string) (out []RenderableObject) { out = make([]RenderableObject, 0) if viper.GetBool("shallow") { @@ -310,26 +244,13 @@ func (r RenderableObject) KubeGetNonTerminatedPodsOnNode(nodeName string) (podLi return } klog.V(5).InfoS("called KubeGetNonTerminatedPodsOnNode", "r", r, "node", nodeName) - podList, err := r.kubeGetNonTerminatedPodsOnTheNode(nodeName) - if err != nil { - klog.V(3).ErrorS(err, "kubeGetNonTerminatedPodsOnTheNode failed", - "r", r, "nodeName", nodeName) - } - return -} - -func (r RenderableObject) kubeGetNonTerminatedPodsOnTheNode(nodeName string) (podList []RenderableObject, err error) { pods, err := r.engine.repo.NonTerminatedPodsOnTheNode(nodeName) if err != nil { klog.V(3).ErrorS(err, "Failed getting non-terminated Pods for Node", "r", r, "nodeName", nodeName) return } - for _, pod := range pods { - nr := r.engine.newRenderableObject(pod) - podList = append(podList, nr) - } - return podList, nil + return r.objectsToRenderableObjects(pods) } // KubeGetUnifiedDiffString generates a unified diff between given 2 resources and ignores several keys which are From 497b4547df22e55dedc7202ae042c01abbba969a Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Sun, 11 Aug 2024 00:26:52 +0100 Subject: [PATCH 11/17] move repo from engine to renderable --- pkg/input/input.go | 4 +-- pkg/plugin/plugin.go | 19 +++++++----- pkg/plugin/render_engine.go | 10 ++----- pkg/plugin/renderable.go | 10 +++++-- pkg/plugin/template_functions_dynamic.go | 38 ++++++++++++------------ pkg/plugin/templates_common_test.go | 7 +++-- 6 files changed, 46 insertions(+), 42 deletions(-) diff --git a/pkg/input/input.go b/pkg/input/input.go index 1b14345..ffcb2c2 100644 --- a/pkg/input/input.go +++ b/pkg/input/input.go @@ -54,8 +54,8 @@ func (u Objects) Swap(i, j int) { u[i], u[j] = u[j], u[i] } -func NewResourceRepo(factory util.Factory) ResourceRepo { - return ResourceRepo{ +func NewResourceRepo(factory util.Factory) *ResourceRepo { + return &ResourceRepo{ f: factory, } } diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 6362071..03c822f 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -17,6 +17,8 @@ import ( "k8s.io/klog/v2" "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util/interrupt" + + "github.com/bergerx/kubectl-status/pkg/input" ) //go:linkname signame runtime.signame @@ -34,18 +36,19 @@ func Run(f util.Factory, streams genericiooptions.IOStreams, args []string) erro } else if viper.Get("color") == "never" { color.NoColor = true } - engine, err := newRenderEngine(f, streams) + repo := input.NewResourceRepo(f) + engine, err := newRenderEngine(streams) if err != nil { klog.V(2).ErrorS(err, "Error creating engine") return err } klog.V(5).InfoS("Created engine", "engine", engine) - results := engine.repo.CLIQueryResults(args) + results := repo.CLIQueryResults(args) count := 0 err = results.Visit(func(resourceInfo *resource.Info, err error) error { count += 1 klog.V(5).InfoS("Processing resource", "item", count, "resource", resourceInfo) - processObj(resourceInfo.Object, engine) + processObj(resourceInfo.Object, engine, repo) return err }) klog.V(5).InfoS("Processed matching resources", "count", count) @@ -58,12 +61,12 @@ func Run(f util.Factory, streams genericiooptions.IOStreams, args []string) erro return fmt.Errorf("no resources found") } if viper.GetBool("watch") { - return runWatch(results, engine) + return runWatch(results, engine, repo) } return nil } -func runWatch(results *resource.Result, engine *renderEngine) error { +func runWatch(results *resource.Result, engine *renderEngine, repo *input.ResourceRepo) error { color.HiYellow("\nPrinted all existing resource statuses, starting to watch. Switching to shallow mode during watch!\n\n") viper.Set("shallow", true) viper.Set("watching", true) @@ -90,7 +93,7 @@ func runWatch(results *resource.Result, engine *renderEngine) error { _ = intr.Run(func() error { _, err := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) { klog.V(5).InfoS("Processing watch event", "e", e) - processObj(e.Object, engine) + processObj(e.Object, engine, repo) return false, nil }) klog.V(1).ErrorS(err, "Watch failed", "obj", obj) @@ -99,7 +102,7 @@ func runWatch(results *resource.Result, engine *renderEngine) error { return nil } -func processObj(obj runtime.Object, engine *renderEngine) { +func processObj(obj runtime.Object, engine *renderEngine, repo *input.ResourceRepo) { streams := engine.ioStreams _, _ = fmt.Fprintf(streams.Out, "\n") out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) @@ -107,7 +110,7 @@ func processObj(obj runtime.Object, engine *renderEngine) { errorPrintf(streams.ErrOut, "Failed to decode obj=%s: %s", obj, err) return } - r := newRenderableObject(out, engine) + r := newRenderableObject(out, engine, repo) err = r.render(streams.Out) if err != nil { _, _ = fmt.Fprintf(streams.ErrOut, "\n") diff --git a/pkg/plugin/render_engine.go b/pkg/plugin/render_engine.go index 766520a..b02c823 100644 --- a/pkg/plugin/render_engine.go +++ b/pkg/plugin/render_engine.go @@ -9,9 +9,6 @@ import ( "github.com/go-sprout/sprout" "k8s.io/cli-runtime/pkg/genericiooptions" "k8s.io/klog/v2" - "k8s.io/kubectl/pkg/cmd/util" - - "github.com/bergerx/kubectl-status/pkg/input" ) //go:embed templates @@ -21,21 +18,18 @@ var templatesFS embed.FS // Also holds the parsed templates. type renderEngine struct { ioStreams genericiooptions.IOStreams - repo input.ResourceRepo template.Template } -func newRenderEngine(f util.Factory, streams genericiooptions.IOStreams) (*renderEngine, error) { - klog.V(5).InfoS("Creating new render engine instance...", "f", f) +func newRenderEngine(streams genericiooptions.IOStreams) (*renderEngine, error) { + klog.V(5).InfoS("Creating new render engine instance...") tmpl, err := getTemplate() - repo := input.NewResourceRepo(f) if err != nil { klog.V(3).ErrorS(err, "Error parsing templates") return nil, err } return &renderEngine{ streams, - repo, *tmpl, }, nil } diff --git a/pkg/plugin/renderable.go b/pkg/plugin/renderable.go index ed6f07a..0aaca90 100644 --- a/pkg/plugin/renderable.go +++ b/pkg/plugin/renderable.go @@ -11,12 +11,15 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" kstatus "sigs.k8s.io/cli-utils/pkg/kstatus/status" + + "github.com/bergerx/kubectl-status/pkg/input" ) -func newRenderableObject(obj map[string]interface{}, engine *renderEngine) RenderableObject { +func newRenderableObject(obj map[string]interface{}, engine *renderEngine, repo *input.ResourceRepo) RenderableObject { r := RenderableObject{ Unstructured: unstructured.Unstructured{Object: obj}, engine: engine, + repo: repo, Config: viper.GetViper(), } return r @@ -28,6 +31,7 @@ func newRenderableObject(obj map[string]interface{}, engine *renderEngine) Rende type RenderableObject struct { unstructured.Unstructured engine *renderEngine + repo *input.ResourceRepo Config *viper.Viper } @@ -40,8 +44,8 @@ func (r RenderableObject) KStatus() *kstatus.Result { return result } -func (e *renderEngine) newRenderableObject(obj map[string]interface{}) RenderableObject { - return newRenderableObject(obj, e) +func (r RenderableObject) newRenderableObject(obj map[string]interface{}) RenderableObject { + return newRenderableObject(obj, r.engine, r.repo) } func (r RenderableObject) String() string { diff --git a/pkg/plugin/template_functions_dynamic.go b/pkg/plugin/template_functions_dynamic.go index 13182bf..7fba2fd 100644 --- a/pkg/plugin/template_functions_dynamic.go +++ b/pkg/plugin/template_functions_dynamic.go @@ -27,7 +27,7 @@ func (r RenderableObject) KubeGet(namespace string, args ...string) (out []Rende return } klog.V(5).InfoS("processing KubeGet", "r", r, "namespace", namespace, "args", args) - objects, err := r.engine.repo.Objects(namespace, args, "") + objects, err := r.repo.Objects(namespace, args, "") if err != nil { klog.V(3).ErrorS(err, "ignoring resource error", "r", r, "namespace", namespace, "args", args) } @@ -36,7 +36,7 @@ func (r RenderableObject) KubeGet(namespace string, args ...string) (out []Rende func (r RenderableObject) objectsToRenderableObjects(objects input.Objects) (out []RenderableObject) { for _, obj := range objects { - nr := r.engine.newRenderableObject(obj) + nr := r.newRenderableObject(obj) out = append(out, nr) } return out @@ -44,14 +44,14 @@ func (r RenderableObject) objectsToRenderableObjects(objects input.Objects) (out // KubeGetFirst returns a new RenderableObject with a nil Object when no object found. func (r RenderableObject) KubeGetFirst(namespace string, args ...string) RenderableObject { - nr := r.engine.newRenderableObject(nil) + nr := r.newRenderableObject(nil) if viper.GetBool("shallow") { return nr } klog.V(5).InfoS("called template method KubeGetFirst", "namespace", namespace, "args", args) var err error - nr.Object, err = r.engine.repo.FirstObject(namespace, args, "") + nr.Object, err = r.repo.FirstObject(namespace, args, "") if err != nil { klog.V(3).ErrorS(err, "KubeGetFirst failed", "namespace", namespace, "args", args) @@ -73,7 +73,7 @@ func (r RenderableObject) KubeGetByLabelsMap(namespace, resourceType string, lab labelPairs = append(labelPairs, fmt.Sprintf("%s=%s", k, v)) } selector := strings.Join(labelPairs, ",") - unstructuredObjects, err := r.engine.repo.Objects(namespace, []string{resourceType}, selector) + unstructuredObjects, err := r.repo.Objects(namespace, []string{resourceType}, selector) if err != nil { klog.V(3).ErrorS(err, "error querying labels", "r", r, "namespace", namespace, "labels", labels) @@ -83,12 +83,12 @@ func (r RenderableObject) KubeGetByLabelsMap(namespace, resourceType string, lab } func (r RenderableObject) KubeGetEvents() RenderableObject { - nr := r.engine.newRenderableObject(nil) + nr := r.newRenderableObject(nil) if viper.GetBool("shallow") { return nr } klog.V(5).InfoS("called KubeGetEvents", "r", r) - eventList, err := r.engine.repo.ObjectEvents(&r.Unstructured) + eventList, err := r.repo.ObjectEvents(&r.Unstructured) if err != nil { klog.V(3).ErrorS(err, "error getting events", "r", r) return nr @@ -104,7 +104,7 @@ func (r RenderableObject) KubeGetOwners() (out []RenderableObject) { return } klog.V(5).InfoS("KubeGetOwners called KubeGetOwners", "r", r) - owners, err := r.engine.repo.Owners(r.Object) + owners, err := r.repo.Owners(r.Object) if err != nil { klog.V(3).ErrorS(err, "error getting owners", "r", r) } @@ -117,7 +117,7 @@ func (r RenderableObject) KubeGetIngressesMatchingService(namespace, svcName str } klog.V(5).InfoS("called KubeGetIngressesMatchingService", "r", r, "namespace", namespace, "svcName", svcName) - ingresses, err := r.engine.repo.Ingresses(namespace) + ingresses, err := r.repo.Ingresses(namespace) if err != nil { klog.V(3).ErrorS(err, "error listing ingresses", "r", r, "namespace", namespace) return @@ -126,7 +126,7 @@ func (r RenderableObject) KubeGetIngressesMatchingService(namespace, svcName str if doesIngressUseService(ing, svcName) { ing.SetGroupVersionKind(netv1.SchemeGroupVersion.WithKind("Ingress")) ingUnstructured, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&ing) - out = append(out, r.engine.newRenderableObject(ingUnstructured)) + out = append(out, r.newRenderableObject(ingUnstructured)) } } return @@ -156,7 +156,7 @@ func (r RenderableObject) KubeGetServicesMatchingLabels(namespace string, labels castedLabels[k] = v.(string) } klog.V(5).InfoS("casted labels values into string", "r", r, "castedLabels", castedLabels) - svcs, err := r.engine.repo.Services(r.Namespace()) + svcs, err := r.repo.Services(r.Namespace()) if err != nil { klog.V(3).ErrorS(err, "error listing services", "r", r, "namespace", namespace) return out @@ -165,7 +165,7 @@ func (r RenderableObject) KubeGetServicesMatchingLabels(namespace string, labels if doesServiceMatchLabels(svc, castedLabels) { svc.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Service")) svcUnstructured, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&svc) - out = append(out, r.engine.newRenderableObject(svcUnstructured)) + out = append(out, r.newRenderableObject(svcUnstructured)) } } return out @@ -177,7 +177,7 @@ func (r RenderableObject) KubeGetServicesMatchingPod(namespace, podName string) return } klog.V(5).InfoS("called KubeGetServicesMatchingPod", "r", r, "namespace", namespace, "podName", podName) - endpoints, err := r.engine.repo.Endpoints(r.Namespace()) + endpoints, err := r.repo.Endpoints(r.Namespace()) if err != nil { klog.V(3).ErrorS(err, "error listing endpoints", "r", r, "namespace", namespace) return @@ -192,14 +192,14 @@ func (r RenderableObject) KubeGetServicesMatchingPod(namespace, podName string) } } if matched { - svc, err := r.engine.repo.Service(r.Namespace(), ep.Name) + svc, err := r.repo.Service(r.Namespace(), ep.Name) if err != nil { klog.V(3).ErrorS(err, "error getting matching service", "r", r, "namespace", namespace, "name", ep.Name) continue } svc.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Service")) svcUnstructured, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(&svc) - out = append(out, r.engine.newRenderableObject(svcUnstructured)) + out = append(out, r.newRenderableObject(svcUnstructured)) } } return out @@ -230,7 +230,7 @@ func (r RenderableObject) KubeGetNodeStatsSummary(nodeName string) map[string]in return nil } klog.V(5).InfoS("called KubeGetNodeStatsSummary", "r", r, "node", nodeName) - nodeStatsSummary, err := r.engine.repo.KubeGetNodeStatsSummary(nodeName) + nodeStatsSummary, err := r.repo.KubeGetNodeStatsSummary(nodeName) if err != nil { klog.V(3).ErrorS(err, "failed to get node stats summary", "r", r, "node", nodeName) return nil @@ -244,7 +244,7 @@ func (r RenderableObject) KubeGetNonTerminatedPodsOnNode(nodeName string) (podLi return } klog.V(5).InfoS("called KubeGetNonTerminatedPodsOnNode", "r", r, "node", nodeName) - pods, err := r.engine.repo.NonTerminatedPodsOnTheNode(nodeName) + pods, err := r.repo.NonTerminatedPodsOnTheNode(nodeName) if err != nil { klog.V(3).ErrorS(err, "Failed getting non-terminated Pods for Node", "r", r, "nodeName", nodeName) @@ -271,7 +271,7 @@ func (r RenderableObject) KubeGetUnifiedDiffString(resourceOrKind, namespace, na } func (r RenderableObject) kubeGetUnifiedDiffString(resourceOrKind, namespace, nameA, nameB string) (string, error) { - gvr, err := r.engine.repo.GVRFor(resourceOrKind) + gvr, err := r.repo.GVRFor(resourceOrKind) if err != nil { klog.V(3).ErrorS(err, "failed to get mapping", "resourceOrKind", resourceOrKind) return "", err @@ -297,7 +297,7 @@ func (r RenderableObject) kubeGetUnifiedDiffString(resourceOrKind, namespace, na } func (r RenderableObject) getObjectDetailsForDiff(gvr schema.GroupVersionResource, namespace string, name string) (string, []byte, time.Time, error) { - object, err := r.engine.repo.DynamicObject(gvr, namespace, name) + object, err := r.repo.DynamicObject(gvr, namespace, name) if err != nil { klog.V(2).ErrorS(err, "failed to query object", "gvr", gvr, "namespace", namespace, "name", name) return "", nil, time.Time{}, err diff --git a/pkg/plugin/templates_common_test.go b/pkg/plugin/templates_common_test.go index b02d9e0..1d862ee 100644 --- a/pkg/plugin/templates_common_test.go +++ b/pkg/plugin/templates_common_test.go @@ -7,13 +7,16 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericiooptions" "k8s.io/kubectl/pkg/cmd/util" + + "github.com/bergerx/kubectl-status/pkg/input" ) func checkTemplate(t *testing.T, templateName string, obj map[string]interface{}, shouldContain string, useRenderable bool) { tmpl, _ := getTemplate() - e, _ := newRenderEngine(util.NewFactory(genericclioptions.NewTestConfigFlags()), genericiooptions.NewTestIOStreamsDiscard()) + repo := input.NewResourceRepo(util.NewFactory(genericclioptions.NewTestConfigFlags())) + e, _ := newRenderEngine(genericiooptions.NewTestIOStreamsDiscard()) e.Template = *tmpl - r := newRenderableObject(obj, e) + r := newRenderableObject(obj, e, repo) var objToPassTemplate interface{} if useRenderable { objToPassTemplate = r From dfea047ecd3a4bf1db28defea23de7d436671829 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Sun, 11 Aug 2024 00:35:13 +0100 Subject: [PATCH 12/17] fix int64/float64 issue in pod_volume_stats template --- pkg/plugin/templates/Pod.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/plugin/templates/Pod.tmpl b/pkg/plugin/templates/Pod.tmpl index 2adaed5..f229326 100644 --- a/pkg/plugin/templates/Pod.tmpl +++ b/pkg/plugin/templates/Pod.tmpl @@ -107,9 +107,9 @@ {{- define "pod_volume_stats" }} {{- /* pod_volume_stats returns a single liner representing all the known volume details */ -}} {{- $bytesUsagePercentOfCapacity := percent (.usedBytes | float64) (.capacityBytes | float64) }} - {{- .usedBytes | humanizeSI "B" }}/{{ .capacityBytes | humanizeSI "B" }}({{- $bytesUsagePercentOfCapacity | colorPercent "%.0f%%" }}) used, {{ .availableBytes | humanizeSI "B" }} free, {{""}} + {{- .usedBytes | float64 | humanizeSI "B" }}/{{ .capacityBytes | float64 | humanizeSI "B" }}({{- $bytesUsagePercentOfCapacity | colorPercent "%.0f%%" }}) used, {{ .availableBytes | float64 | humanizeSI "B" }} free, {{""}} {{- $inodesUsagePercentOfCapacity := percent (.inodesUsed | float64) (.inodes | float64) }} - {{- .inodesUsed | humanizeSI "" }}/{{ .inodes | humanizeSI "" }}({{- $inodesUsagePercentOfCapacity | colorPercent "%.0f%%" }}) inodes used, {{ .inodesFree | humanizeSI "" }} free. + {{- .inodesUsed | float64 | humanizeSI "" }}/{{ .inodes | float64 | humanizeSI "" }}({{- $inodesUsagePercentOfCapacity | colorPercent "%.0f%%" }}) inodes used, {{ .inodesFree | float64 | humanizeSI "" }} free. {{- end }} {{- define "matching_services" }} From e25445c9f72dcea1891f588181a47e1f7928c914 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Sun, 11 Aug 2024 20:00:06 +0100 Subject: [PATCH 13/17] simplify repo by moving client initialisation to the new method --- pkg/input/input.go | 97 ++++++++--------------------- pkg/plugin/plugin.go | 6 +- pkg/plugin/templates_common_test.go | 12 +++- 3 files changed, 39 insertions(+), 76 deletions(-) diff --git a/pkg/input/input.go b/pkg/input/input.go index ffcb2c2..048e0e3 100644 --- a/pkg/input/input.go +++ b/pkg/input/input.go @@ -54,17 +54,26 @@ func (u Objects) Swap(i, j int) { u[i], u[j] = u[j], u[i] } -func NewResourceRepo(factory util.Factory) *ResourceRepo { - return &ResourceRepo{ - f: factory, +func NewResourceRepo(factory util.Factory) (*ResourceRepo, error) { + dynamicClient, err := factory.DynamicClient() + if err != nil { + return nil, err + } + kubernetesClientSet, err := factory.KubernetesClientSet() + if err != nil { + return nil, err } + return &ResourceRepo{ + f: factory, + dynamicClient: dynamicClient, + kubernetesClientSet: kubernetesClientSet, + }, nil } type ResourceRepo struct { - f util.Factory - - client dynamic.Interface - clientSet *kubernetes.Clientset + f util.Factory + dynamicClient dynamic.Interface + kubernetesClientSet *kubernetes.Clientset } func (r *ResourceRepo) newBaseBuilder() *resource.Builder { @@ -184,12 +193,7 @@ func (r *ResourceRepo) FirstObject(namespace string, args []string, labelSelecto } func (r *ResourceRepo) ObjectEvents(u *unstructured.Unstructured) (*corev1.EventList, error) { - clientSet, err := r.kubernetesClientSet() - if err != nil { - klog.V(3).ErrorS(err, "error getting events", "r", r) - return nil, err - } - eventList, err := clientSet.CoreV1().Events(u.GetNamespace()).Search(scheme.Scheme, u) + eventList, err := r.kubernetesClientSet.CoreV1().Events(u.GetNamespace()).Search(scheme.Scheme, u) if err != nil { klog.V(3).ErrorS(err, "error getting events", "r", r) return nil, err @@ -198,34 +202,8 @@ func (r *ResourceRepo) ObjectEvents(u *unstructured.Unstructured) (*corev1.Event return eventList, nil } -func (r *ResourceRepo) kubernetesClientSet() (*kubernetes.Clientset, error) { - if r.clientSet == nil { - var err error - r.clientSet, err = r.f.KubernetesClientSet() - if err != nil { - return nil, err - } - } - return r.clientSet, nil -} - -func (r *ResourceRepo) dynamicClient() (dynamic.Interface, error) { - if r.client == nil { - var err error - r.client, err = r.f.DynamicClient() - if err != nil { - return nil, err - } - } - return r.client, nil -} - func (r *ResourceRepo) DynamicObject(gvr schema.GroupVersionResource, namespace string, name string) (Object, error) { - dynamicClient, err := r.dynamicClient() - if err != nil { - return nil, err - } - u, err := dynamicClient.Resource(gvr).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{}) + u, err := r.dynamicClient.Resource(gvr).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{}) if err != nil { return nil, err } @@ -237,11 +215,7 @@ func (r *ResourceRepo) DynamicObject(gvr schema.GroupVersionResource, namespace } func (r *ResourceRepo) DynamicObjects(gvr schema.GroupVersionResource, namespace string) (Objects, error) { - dynamicClient, err := r.dynamicClient() - if err != nil { - return nil, err - } - unstructuredList, err := dynamicClient.Resource(gvr).Namespace(namespace).List(context.TODO(), metav1.ListOptions{}) + unstructuredList, err := r.dynamicClient.Resource(gvr).Namespace(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { return nil, err } @@ -312,46 +286,26 @@ func (r *ResourceRepo) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, } func (r *ResourceRepo) Ingresses(namespace string) (*netv1.IngressList, error) { - clientSet, err := r.kubernetesClientSet() - if err != nil { - return nil, err - } - return clientSet.NetworkingV1().Ingresses(namespace).List(context.TODO(), metav1.ListOptions{}) + return r.kubernetesClientSet.NetworkingV1().Ingresses(namespace).List(context.TODO(), metav1.ListOptions{}) } func (r *ResourceRepo) Services(namespace string) (*corev1.ServiceList, error) { - clientSet, err := r.kubernetesClientSet() - if err != nil { - return nil, err - } - return clientSet.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{}) + return r.kubernetesClientSet.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{}) } func (r *ResourceRepo) Service(namespace, name string) (*corev1.Service, error) { - clientSet, err := r.kubernetesClientSet() - if err != nil { - return nil, err - } - return clientSet.CoreV1().Services(namespace).Get(context.TODO(), name, metav1.GetOptions{}) + return r.kubernetesClientSet.CoreV1().Services(namespace).Get(context.TODO(), name, metav1.GetOptions{}) } func (r *ResourceRepo) Endpoints(namespace string) (*corev1.EndpointsList, error) { - clientSet, err := r.kubernetesClientSet() - if err != nil { - return nil, err - } - return clientSet.CoreV1().Endpoints(namespace).List(context.TODO(), metav1.ListOptions{}) + return r.kubernetesClientSet.CoreV1().Endpoints(namespace).List(context.TODO(), metav1.ListOptions{}) } // KubeGetNodeStatsSummary returns this structure // > kubectl get --raw /api/v1/nodes/{nodeName}/proxy/stats/summary // The endpoint that this function uses will be disabled soon: https://github.com/kubernetes/kubernetes/issues/68522 func (r *ResourceRepo) KubeGetNodeStatsSummary(nodeName string) (Object, error) { - clientSet, err := r.kubernetesClientSet() - if err != nil { - return nil, err - } - getBytes, err := clientSet.CoreV1().RESTClient().Get(). + getBytes, err := r.kubernetesClientSet.CoreV1().RESTClient().Get(). Resource("nodes"). SubResource("proxy"). Name(nodeName). @@ -366,7 +320,6 @@ func (r *ResourceRepo) KubeGetNodeStatsSummary(nodeName string) (Object, error) } func (r *ResourceRepo) NonTerminatedPodsOnTheNode(nodeName string) (Objects, error) { - clientSet, _ := r.kubernetesClientSet() fieldSelector, err := fields.ParseSelector("spec.nodeName=" + nodeName + ",status.phase!=" + string(corev1.PodSucceeded) + ",status.phase!=" + string(corev1.PodFailed)) @@ -375,7 +328,7 @@ func (r *ResourceRepo) NonTerminatedPodsOnTheNode(nodeName string) (Objects, err "r", r, "nodeName", nodeName) return nil, err } - nodeNonTerminatedPodsList, err := clientSet.CoreV1(). + nodeNonTerminatedPodsList, err := r.kubernetesClientSet.CoreV1(). Pods(""). // Search in all namespaces List(context.TODO(), metav1.ListOptions{FieldSelector: fieldSelector.String()}) if err != nil { diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 03c822f..8bed8e3 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -36,7 +36,11 @@ func Run(f util.Factory, streams genericiooptions.IOStreams, args []string) erro } else if viper.Get("color") == "never" { color.NoColor = true } - repo := input.NewResourceRepo(f) + repo, err := input.NewResourceRepo(f) + if err != nil { + klog.V(2).ErrorS(err, "Error creating repo") + return err + } engine, err := newRenderEngine(streams) if err != nil { klog.V(2).ErrorS(err, "Error creating engine") diff --git a/pkg/plugin/templates_common_test.go b/pkg/plugin/templates_common_test.go index 1d862ee..664765c 100644 --- a/pkg/plugin/templates_common_test.go +++ b/pkg/plugin/templates_common_test.go @@ -4,16 +4,21 @@ import ( "strings" "testing" - "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericiooptions" - "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/client-go/rest/fake" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" "github.com/bergerx/kubectl-status/pkg/input" ) func checkTemplate(t *testing.T, templateName string, obj map[string]interface{}, shouldContain string, useRenderable bool) { + t.Helper() tmpl, _ := getTemplate() - repo := input.NewResourceRepo(util.NewFactory(genericclioptions.NewTestConfigFlags())) + f := cmdtesting.NewTestFactory().WithNamespace("test") + f.Client = &fake.RESTClient{} + f.UnstructuredClient = f.Client + t.Cleanup(func() { f.Cleanup() }) + repo, _ := input.NewResourceRepo(f) e, _ := newRenderEngine(genericiooptions.NewTestIOStreamsDiscard()) e.Template = *tmpl r := newRenderableObject(obj, e, repo) @@ -118,6 +123,7 @@ func TestSuspendTemplate(t *testing.T) { }) } } + func TestOwnersTemplate(t *testing.T) { tests := []struct { name string From 2ec4064d2c5e65e904c8f785759382c77147d7fb Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Mon, 12 Aug 2024 14:36:36 +0100 Subject: [PATCH 14/17] minor changes --- .goreleaser.yml | 2 ++ cmd/main.go | 6 ++++-- cmd/main_test.go | 3 ++- go.mod | 1 - go.sum | 2 -- pkg/plugin/templates_common_test.go | 1 + staticcheck.conf | 1 + 7 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 staticcheck.conf diff --git a/.goreleaser.yml b/.goreleaser.yml index 89e863b..c7a823e 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,4 +1,5 @@ project_name: status +version: 2 release: github: owner: bergerx @@ -6,6 +7,7 @@ release: before: hooks: - go generate ./... + - go mod tidy builds: - id: status goos: diff --git a/cmd/main.go b/cmd/main.go index 5ef097e..d109b30 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -151,11 +151,13 @@ func initColorCobra(cmd *cobra.Command) { } func hideNoisyFlags(flags *pflag.FlagSet) { - flagsToHide := []string{"add_dir_header", "as-uid", "alsologtostderr", "as", "as-group", "cache-dir", + flagsToHide := []string{ + "add_dir_header", "as-uid", "alsologtostderr", "as", "as-group", "cache-dir", "certificate-authority", "client-certificate", "client-key", "cluster", "context", "insecure-skip-tls-verify", "kubeconfig", "log_backtrace_at", "log_dir", "log_file", "log_file_max_size", "logtostderr", "one_output", "password", "request-timeout", "server", "skip_headers", "skip_log_headers", "stderrthreshold", - "tls-server-name", "token", "user", "username", "vmodule", "test-hack"} + "tls-server-name", "token", "user", "username", "vmodule", "test-hack", + } for _, flagName := range flagsToHide { flags.Lookup(flagName).Hidden = true } diff --git a/cmd/main_test.go b/cmd/main_test.go index 64d7562..c9dafb2 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -38,6 +38,7 @@ type cmdTest struct { func nodeNameModifier(stdout string) string { return string(regexp.MustCompile(`Node/[a-z0-9-]+`).ReplaceAll([]byte(stdout), []byte(`Node/minikube`))) } + func (c cmdTest) assert(t *testing.T, stdoutModifier func(string) string) { t.Helper() t.Logf("running cmdTest assert: %s", c) @@ -188,7 +189,6 @@ func viperTestHack(t *testing.T) { t.Cleanup(func() { viper.Reset() }) - } func TestAllArtifactsLocal(t *testing.T) { @@ -246,6 +246,7 @@ func startMinikube(t *testing.T) { } }) } + func e2eMinikubeTest(t *testing.T) { t.Helper() if os.Getenv("RUN_E2E_TESTS") != "true" { diff --git a/go.mod b/go.mod index 61a439e..45d7917 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,6 @@ require ( github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fvbommel/sortorder v1.1.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect diff --git a/go.sum b/go.sum index 5aa473a..7f30eba 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,6 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= -github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= diff --git a/pkg/plugin/templates_common_test.go b/pkg/plugin/templates_common_test.go index 664765c..be56b8b 100644 --- a/pkg/plugin/templates_common_test.go +++ b/pkg/plugin/templates_common_test.go @@ -87,6 +87,7 @@ func TestObservedGenerationSummaryTemplate(t *testing.T) { }) } } + func TestSuspendTemplate(t *testing.T) { tests := []struct { name string diff --git a/staticcheck.conf b/staticcheck.conf new file mode 100644 index 0000000..5f0a348 --- /dev/null +++ b/staticcheck.conf @@ -0,0 +1 @@ +checks = ["all", "-ST1000"] From ac7d0138b5c8f4940dd147d5d8eac3e36d62a54b Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Sun, 29 Sep 2024 00:58:36 +0100 Subject: [PATCH 15/17] remove go:linkname for signal names as go1.23 disallows it --- Makefile | 2 +- go.mod | 2 +- pkg/plugin/plugin.go | 3 --- pkg/plugin/template_functions_static.go | 4 +++- tests/artifacts/pod-deleted-due-to-missing-container.out | 4 ++-- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index b4cc083..78be965 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ vet: .PHONY: staticcheck staticcheck: - go run honnef.co/go/tools/cmd/staticcheck@v0.4.7 ./... + go run honnef.co/go/tools/cmd/staticcheck@v0.5.1 ./... .PHONY: clean clean: diff --git a/go.mod b/go.mod index 45d7917..2626654 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 + golang.org/x/sys v0.22.0 k8s.io/api v0.30.3 k8s.io/apimachinery v0.30.3 k8s.io/cli-runtime v0.30.3 @@ -87,7 +88,6 @@ require ( golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.22.0 // indirect golang.org/x/term v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 8bed8e3..6bd0717 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -21,9 +21,6 @@ import ( "github.com/bergerx/kubectl-status/pkg/input" ) -//go:linkname signame runtime.signame -func signame(sig uint32) string - func errorPrintf(wr io.Writer, format string, a ...interface{}) { _, _ = color.New(color.BgRed, color.FgHiWhite).Printf(format, a...) _, _ = fmt.Fprintln(wr) diff --git a/pkg/plugin/template_functions_static.go b/pkg/plugin/template_functions_static.go index 2ad85b9..c6bcc65 100644 --- a/pkg/plugin/template_functions_static.go +++ b/pkg/plugin/template_functions_static.go @@ -6,6 +6,7 @@ import ( "regexp" "sort" "strings" + "syscall" "text/template" "time" @@ -14,6 +15,7 @@ import ( "github.com/go-sprout/sprout" "github.com/spf13/cast" "github.com/spf13/viper" + "golang.org/x/sys/unix" resource2 "k8s.io/apimachinery/pkg/api/resource" "k8s.io/klog/v2" ) @@ -281,7 +283,7 @@ func isStatusConditionHealthy(condition map[string]interface{}) bool { } func signalName(signal int64) string { - return signame(uint32(signal)) + return unix.SignalName(syscall.Signal(signal)) } func redIf(cond interface{}, str string) string { diff --git a/tests/artifacts/pod-deleted-due-to-missing-container.out b/tests/artifacts/pod-deleted-due-to-missing-container.out index 6dd8a7c..024e835 100644 --- a/tests/artifacts/pod-deleted-due-to-missing-container.out +++ b/tests/artifacts/pod-deleted-due-to-missing-container.out @@ -5,5 +5,5 @@ Pod/prometheus-operator-5c5784bc5f-4h65z -n prometheus, created 1m ago by Replic Ready PodFailed for 1m ContainersReady PodFailed for 1m Containers: - kube-prometheus-stack (quay.io/prometheus-operator/prometheus-operator:v0.58.0) Terminated as ContainerStatusUnknown with "The container could not be located when the pod was terminated" exit with 137 (SIGKILL: kill), restarted 1 times - previously: Terminated as ContainerStatusUnknown with "The container could not be located when the pod was deleted. The container used to be Running" exit with 137 (SIGKILL: kill) + kube-prometheus-stack (quay.io/prometheus-operator/prometheus-operator:v0.58.0) Terminated as ContainerStatusUnknown with "The container could not be located when the pod was terminated" exit with 137 (SIGKILL), restarted 1 times + previously: Terminated as ContainerStatusUnknown with "The container could not be located when the pod was deleted. The container used to be Running" exit with 137 (SIGKILL) From 630bedfb67b8c03e8abecb0de64206bef5099921 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Sun, 29 Sep 2024 05:23:20 +0100 Subject: [PATCH 16/17] upgrade k8s libraries --- go.mod | 33 ++++++++++---------- go.sum | 99 +++++++++++++++++++++++++--------------------------------- 2 files changed, 59 insertions(+), 73 deletions(-) diff --git a/go.mod b/go.mod index 2626654..6a3f291 100644 --- a/go.mod +++ b/go.mod @@ -14,12 +14,12 @@ require ( github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 golang.org/x/sys v0.22.0 - k8s.io/api v0.30.3 - k8s.io/apimachinery v0.30.3 - k8s.io/cli-runtime v0.30.3 - k8s.io/client-go v0.30.3 + k8s.io/api v0.31.1 + k8s.io/apimachinery v0.31.1 + k8s.io/cli-runtime v0.31.1 + k8s.io/client-go v0.31.1 k8s.io/klog/v2 v2.130.1 - k8s.io/kubectl v0.30.3 + k8s.io/kubectl v0.31.1 sigs.k8s.io/cli-utils v0.37.2 ) @@ -28,15 +28,16 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect @@ -64,8 +65,8 @@ require ( github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/moby/spdystream v0.2.0 // indirect - github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect + github.com/moby/spdystream v0.4.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect @@ -80,29 +81,29 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xlab/treeprint v1.2.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/term v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/evanphx/json-patch.v5 v5.6.0 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/component-base v0.30.3 // indirect + k8s.io/component-base v0.31.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/kustomize/api v0.15.0 // indirect + sigs.k8s.io/kustomize/api v0.17.2 // indirect sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 7f30eba..eda60a9 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= @@ -32,8 +34,6 @@ github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxER github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= -github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= @@ -45,10 +45,12 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= @@ -58,7 +60,6 @@ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogB github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-sprout/sprout v0.4.1 h1:grvsR21YepGs64EFoIXg4g+5OzIZFwmsw5Y88Wod9sI= github.com/go-sprout/sprout v0.4.1/go.mod h1:jRgO0n+24zLgiPAg/6rMaeq2oEnBSGlZiHUoK3hnQc4= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -73,8 +74,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= @@ -87,20 +86,18 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= -github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= @@ -129,6 +126,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -149,10 +148,10 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= +github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -178,8 +177,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= @@ -216,19 +215,21 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -239,7 +240,6 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -248,19 +248,16 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -269,26 +266,19 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -301,7 +291,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -310,8 +299,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= @@ -326,15 +313,13 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v5 v5.6.0 h1:BMT6KIwBD9CaU91PJCZIe46bDmBWa9ynTQgJIOpfQBk= -gopkg.in/evanphx/json-patch.v5 v5.6.0/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= @@ -348,30 +333,30 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.30.3 h1:ImHwK9DCsPA9uoU3rVh4QHAHHK5dTSv1nxJUapx8hoQ= -k8s.io/api v0.30.3/go.mod h1:GPc8jlzoe5JG3pb0KJCSLX5oAFIW3/qNJITlDj8BH04= -k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc= -k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= -k8s.io/cli-runtime v0.30.3 h1:aG69oRzJuP2Q4o8dm+f5WJIX4ZBEwrvdID0+MXyUY6k= -k8s.io/cli-runtime v0.30.3/go.mod h1:hwrrRdd9P84CXSKzhHxrOivAR9BRnkMt0OeP5mj7X30= -k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k= -k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U= -k8s.io/component-base v0.30.3 h1:Ci0UqKWf4oiwy8hr1+E3dsnliKnkMLZMVbWzeorlk7s= -k8s.io/component-base v0.30.3/go.mod h1:C1SshT3rGPCuNtBs14RmVD2xW0EhRSeLvBh7AGk1quA= +k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= +k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= +k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk= +k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U= +k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= +k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= +k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= +k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/kubectl v0.30.3 h1:YIBBvMdTW0xcDpmrOBzcpUVsn+zOgjMYIu7kAq+yqiI= -k8s.io/kubectl v0.30.3/go.mod h1:IcR0I9RN2+zzTRUa1BzZCm4oM0NLOawE6RzlDvd1Fpo= -k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak= -k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kubectl v0.31.1 h1:ih4JQJHxsEggFqDJEHSOdJ69ZxZftgeZvYo7M/cpp24= +k8s.io/kubectl v0.31.1/go.mod h1:aNuQoR43W6MLAtXQ/Bu4GDmoHlbhHKuyD49lmTC8eJM= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/cli-utils v0.37.2 h1:GOfKw5RV2HDQZDJlru5KkfLO1tbxqMoyn1IYUxqBpNg= sigs.k8s.io/cli-utils v0.37.2/go.mod h1:V+IZZr4UoGj7gMJXklWBg6t5xbdThFBcpj4MrZuCYco= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/api v0.15.0 h1:6Ca88kEOBVotHDw+y2IsIMYtg9Pvv7MKpW9JMyF/OH4= -sigs.k8s.io/kustomize/api v0.15.0/go.mod h1:p19kb+E14gN7zcIBR/nhByJDAfUa7N8mp6ZdH/mMXbg= +sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= +sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0= sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ= sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= From 1e84d755a42e9c9c5b3b381e7741eeb979947c19 Mon Sep 17 00:00:00 2001 From: Bekir Dogan Date: Thu, 24 Oct 2024 00:53:15 +0100 Subject: [PATCH 17/17] update dependencies --- cmd/main_test.go | 4 ++-- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/cmd/main_test.go b/cmd/main_test.go index c9dafb2..4495aa7 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -89,7 +89,7 @@ func TestRootCmdWithoutACluster(t *testing.T) { { name: "pods against a non-configured client should print an error", args: []string{"pods"}, - stderrRegex: `The connection to the server localhost:8080 was refused`, + stderrRegex: `the server rejected our request for an unknown reason`, }, { name: "missing file should fail", @@ -99,7 +99,7 @@ func TestRootCmdWithoutACluster(t *testing.T) { { name: "file without local should fail", args: []string{"-f", "../tests/artifacts/deployment-healthy.yaml"}, - stderrRegex: `dial tcp \[::1\]:8080: connect: connection refused\n$`, + stderrRegex: `the server rejected our request for an unknown reason\n$`, }, { name: "file with local should succeed", diff --git a/go.mod b/go.mod index 6a3f291..557dda6 100644 --- a/go.mod +++ b/go.mod @@ -14,12 +14,12 @@ require ( github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 golang.org/x/sys v0.22.0 - k8s.io/api v0.31.1 - k8s.io/apimachinery v0.31.1 - k8s.io/cli-runtime v0.31.1 - k8s.io/client-go v0.31.1 + k8s.io/api v0.31.2 + k8s.io/apimachinery v0.31.2 + k8s.io/cli-runtime v0.31.2 + k8s.io/client-go v0.31.2 k8s.io/klog/v2 v2.130.1 - k8s.io/kubectl v0.31.1 + k8s.io/kubectl v0.31.2 sigs.k8s.io/cli-utils v0.37.2 ) @@ -99,7 +99,7 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/component-base v0.31.1 // indirect + k8s.io/component-base v0.31.2 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/go.sum b/go.sum index eda60a9..d347034 100644 --- a/go.sum +++ b/go.sum @@ -333,22 +333,22 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= -k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= -k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= -k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk= -k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U= -k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= -k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= -k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= -k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= +k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= +k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= +k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ= +k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q= +k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= +k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= +k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA= +k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/kubectl v0.31.1 h1:ih4JQJHxsEggFqDJEHSOdJ69ZxZftgeZvYo7M/cpp24= -k8s.io/kubectl v0.31.1/go.mod h1:aNuQoR43W6MLAtXQ/Bu4GDmoHlbhHKuyD49lmTC8eJM= +k8s.io/kubectl v0.31.2 h1:gTxbvRkMBwvTSAlobiTVqsH6S8Aa1aGyBcu5xYLsn8M= +k8s.io/kubectl v0.31.2/go.mod h1:EyASYVU6PY+032RrTh5ahtSOMgoDRIux9V1JLKtG5xM= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/cli-utils v0.37.2 h1:GOfKw5RV2HDQZDJlru5KkfLO1tbxqMoyn1IYUxqBpNg=