From 6f7ac066ae8a487621c169a5e588ebd4a19df284 Mon Sep 17 00:00:00 2001 From: Austin Abro Date: Mon, 23 Dec 2024 22:29:22 +0000 Subject: [PATCH] extending factory to enable getting a watcher Signed-off-by: Austin Abro --- pkg/kube/client.go | 45 ++++++++++++++++++++++++++++++++--------- pkg/kube/client_test.go | 5 +++++ pkg/kube/factory.go | 6 ++++++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 8bcd4824f..a25a6fcc3 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -43,6 +43,7 @@ import ( "sigs.k8s.io/cli-utils/pkg/kstatus/status" "sigs.k8s.io/cli-utils/pkg/kstatus/watcher" "sigs.k8s.io/cli-utils/pkg/object" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" multierror "github.com/hashicorp/go-multierror" "k8s.io/apimachinery/pkg/api/meta" @@ -56,6 +57,7 @@ import ( "k8s.io/apimachinery/pkg/watch" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" + "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" @@ -289,17 +291,43 @@ func getResource(info *resource.Info) (runtime.Object, error) { // Wait waits up to the given timeout for the specified resources to be ready. func (c *Client) Wait(resources ResourceList, timeout time.Duration) error { - cs, err := c.getKubeClient() + // cs, err := c.getKubeClient() + // if err != nil { + // return err + // } + // checker := NewReadyChecker(cs, c.Log, PausedAsReady(true)) + // w := waiter{ + // c: checker, + // log: c.Log, + // timeout: timeout, + // } + cfg, err := c.Factory.ToRESTConfig() if err != nil { return err } - checker := NewReadyChecker(cs, c.Log, PausedAsReady(true)) - w := waiter{ - c: checker, - log: c.Log, - timeout: timeout, + dynamicClient, err := dynamic.NewForConfig(cfg) + if err != nil { + return err } - return w.waitForResources(resources) + // Not sure if I should use factory methods to get this http client or I should do this + // For example, I could likely use this as well, but it seems like I should use the factory methods instead + // httpClient, err := rest.HTTPClientFor(cfg) + // if err != nil { + // return err + // } + client, err := c.Factory.RESTClient() + if err != nil { + return err + } + restMapper, err := apiutil.NewDynamicRESTMapper(cfg, client.Client) + if err != nil { + return err + } + sw := watcher.NewDefaultStatusWatcher(dynamicClient, restMapper) + // return sw, nil + ctx, cancel := context.WithTimeout(context.TODO(), timeout) + defer cancel() + return WaitForReady(ctx, sw, resources) } // WaitForReady waits for all of the objects to reach a ready state. @@ -319,7 +347,6 @@ func WaitForReady(ctx context.Context, sw watcher.StatusWatcher, resourceList Re } resources = append(resources, obj) } - eventCh := sw.Watch(cancelCtx, resources, watcher.Options{}) statusCollector := collector.NewResourceStatusCollector(resources) done := statusCollector.ListenWithObserver(eventCh, collector.ObserverFunc( @@ -346,7 +373,6 @@ func WaitForReady(ctx context.Context, sw watcher.StatusWatcher, resourceList Re // Only check parent context error, otherwise we would error when desired status is achieved. if ctx.Err() != nil { - // todo use err var err error for _, id := range resources { rs := statusCollector.ResourceStatuses[id] @@ -357,7 +383,6 @@ func WaitForReady(ctx context.Context, sw watcher.StatusWatcher, resourceList Re } return fmt.Errorf("not all resources ready: %w: %w", ctx.Err(), err) } - return nil } diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index f2d6bcb59..7f3ba65be 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -453,12 +453,17 @@ func TestPerform(t *testing.T) { } } +// Likely it is not possible to get this test to work with kstatus given that it seems +// kstatus is not making constant get checks on the resources and is instead waiting for events +// Potentially the test could be reworked to make the pods after five seconds +// would need this -> func TestWait(t *testing.T) { podList := newPodList("starfish", "otter", "squid") var created *time.Time c := newTestClient(t) + c.Factory.(*cmdtesting.TestFactory).ClientConfigVal = cmdtesting.DefaultClientConfig() c.Factory.(*cmdtesting.TestFactory).Client = &fake.RESTClient{ NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { diff --git a/pkg/kube/factory.go b/pkg/kube/factory.go index f19d62dc3..b0b506282 100644 --- a/pkg/kube/factory.go +++ b/pkg/kube/factory.go @@ -17,9 +17,11 @@ limitations under the License. package kube // import "helm.sh/helm/v3/pkg/kube" import ( + "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" + restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/kubectl/pkg/validation" ) @@ -33,6 +35,7 @@ import ( // Helm does not need are not impacted or exposed. This minimizes the impact of Kubernetes changes // being exposed. type Factory interface { + genericclioptions.RESTClientGetter // ToRawKubeConfigLoader return kubeconfig loader as-is ToRawKubeConfigLoader() clientcmd.ClientConfig @@ -42,6 +45,9 @@ type Factory interface { // KubernetesClientSet gives you back an external clientset KubernetesClientSet() (*kubernetes.Clientset, error) + // Returns a RESTClient for accessing Kubernetes resources or an error. + RESTClient() (*restclient.RESTClient, error) + // NewBuilder returns an object that assists in loading objects from both disk and the server // and which implements the common patterns for CLI interactions with generic resources. NewBuilder() *resource.Builder