diff --git a/cluster-autoscaler/config/autoscaling_options.go b/cluster-autoscaler/config/autoscaling_options.go index 5eb2e510e912..0a2fa841f9ab 100644 --- a/cluster-autoscaler/config/autoscaling_options.go +++ b/cluster-autoscaler/config/autoscaling_options.go @@ -275,6 +275,8 @@ type AutoscalingOptions struct { DynamicNodeDeleteDelayAfterTaintEnabled bool // BypassedSchedulers are used to specify which schedulers to bypass their processing BypassedSchedulers map[string]bool + // ProvisioningRequestEnabled tells if CA processes ProvisioningRequest. + ProvisioningRequestEnabled bool } // KubeClientOptions specify options for kube client diff --git a/cluster-autoscaler/core/podlistprocessor/pod_list_processor.go b/cluster-autoscaler/core/podlistprocessor/pod_list_processor.go index ccbc5dfc8183..f14860983475 100644 --- a/cluster-autoscaler/core/podlistprocessor/pod_list_processor.go +++ b/cluster-autoscaler/core/podlistprocessor/pod_list_processor.go @@ -17,21 +17,15 @@ limitations under the License. package podlistprocessor import ( - apiv1 "k8s.io/api/core/v1" - "k8s.io/autoscaler/cluster-autoscaler/context" "k8s.io/autoscaler/cluster-autoscaler/processors/pods" "k8s.io/autoscaler/cluster-autoscaler/simulator/predicatechecker" ) -type defaultPodListProcessor struct { - processors []pods.PodListProcessor -} - // NewDefaultPodListProcessor returns a default implementation of the pod list // processor, which wraps and sequentially runs other sub-processors. -func NewDefaultPodListProcessor(predicateChecker predicatechecker.PredicateChecker) *defaultPodListProcessor { - return &defaultPodListProcessor{ - processors: []pods.PodListProcessor{ +func NewDefaultPodListProcessor(predicateChecker predicatechecker.PredicateChecker) *pods.ListedPodListProcessor { + return &pods.ListedPodListProcessor{ + Processors: []pods.PodListProcessor{ NewClearTPURequestsPodListProcessor(), NewFilterOutExpendablePodListProcessor(), NewCurrentlyDrainedNodesPodListProcessor(), @@ -40,21 +34,3 @@ func NewDefaultPodListProcessor(predicateChecker predicatechecker.PredicateCheck }, } } - -// Process runs sub-processors sequentially -func (p *defaultPodListProcessor) Process(ctx *context.AutoscalingContext, unschedulablePods []*apiv1.Pod) ([]*apiv1.Pod, error) { - var err error - for _, processor := range p.processors { - unschedulablePods, err = processor.Process(ctx, unschedulablePods) - if err != nil { - return nil, err - } - } - return unschedulablePods, nil -} - -func (p *defaultPodListProcessor) CleanUp() { - for _, processor := range p.processors { - processor.CleanUp() - } -} diff --git a/cluster-autoscaler/go.mod b/cluster-autoscaler/go.mod index 8331f14305ca..2c2823e05990 100644 --- a/cluster-autoscaler/go.mod +++ b/cluster-autoscaler/go.mod @@ -1,6 +1,6 @@ module k8s.io/autoscaler/cluster-autoscaler -go 1.21 +go 1.21.3 require ( cloud.google.com/go/compute/metadata v0.2.3 diff --git a/cluster-autoscaler/main.go b/cluster-autoscaler/main.go index 0d54704a4619..3b8ef439e480 100644 --- a/cluster-autoscaler/main.go +++ b/cluster-autoscaler/main.go @@ -47,6 +47,7 @@ import ( ca_processors "k8s.io/autoscaler/cluster-autoscaler/processors" "k8s.io/autoscaler/cluster-autoscaler/processors/nodegroupset" "k8s.io/autoscaler/cluster-autoscaler/processors/nodeinfosprovider" + "k8s.io/autoscaler/cluster-autoscaler/processors/provreq" "k8s.io/autoscaler/cluster-autoscaler/processors/scaledowncandidates" "k8s.io/autoscaler/cluster-autoscaler/processors/scaledowncandidates/emptycandidates" "k8s.io/autoscaler/cluster-autoscaler/processors/scaledowncandidates/previouscandidates" @@ -241,6 +242,7 @@ var ( forceDaemonSets = flag.Bool("force-ds", false, "Blocks scale-up of node groups too small for all suitable Daemon Sets pods.") dynamicNodeDeleteDelayAfterTaintEnabled = flag.Bool("dynamic-node-delete-delay-after-taint-enabled", false, "Enables dynamic adjustment of NodeDeleteDelayAfterTaint based of the latency between CA and api-server") bypassedSchedulers = pflag.StringSlice("bypassed-scheduler-names", []string{}, fmt.Sprintf("Names of schedulers to bypass. If set to non-empty value, CA will not wait for pods to reach a certain age before triggering a scale-up.")) + provisioningRequestsEnabled = flag.Bool("enable-provisioning-requests", false, "Whether the clusterautoscaler will be handling the ProvisioningRequest CRs.") ) func isFlagPassed(name string) bool { @@ -398,6 +400,7 @@ func createAutoscalingOptions() config.AutoscalingOptions { }, DynamicNodeDeleteDelayAfterTaintEnabled: *dynamicNodeDeleteDelayAfterTaintEnabled, BypassedSchedulers: scheduler_util.GetBypassedSchedulersMap(*bypassedSchedulers), + ProvisioningRequestEnabled: *provisioningRequestsEnabled, } } @@ -451,7 +454,11 @@ func buildAutoscaler(debuggingSnapshotter debuggingsnapshot.DebuggingSnapshotter opts.Processors = ca_processors.DefaultProcessors(autoscalingOptions) opts.Processors.TemplateNodeInfoProvider = nodeinfosprovider.NewDefaultTemplateNodeInfoProvider(nodeInfoCacheExpireTime, *forceDaemonSets) - opts.Processors.PodListProcessor = podlistprocessor.NewDefaultPodListProcessor(opts.PredicateChecker) + podListProcessor := podlistprocessor.NewDefaultPodListProcessor(opts.PredicateChecker) + if autoscalingOptions.ProvisioningRequestEnabled { + podListProcessor.AppendProcessor(provreq.NewProvisioningRequestPodsFilter(provreq.NewDefautlEventManager())) + } + opts.Processors.PodListProcessor = podListProcessor scaleDownCandidatesComparers := []scaledowncandidates.CandidatesComparer{} if autoscalingOptions.ParallelDrain { sdCandidatesSorting := previouscandidates.NewPreviousCandidates() diff --git a/cluster-autoscaler/processors/pods/pod_list_processor.go b/cluster-autoscaler/processors/pods/pod_list_processor.go index 13efba7d6d42..cd3a5a3fd45e 100644 --- a/cluster-autoscaler/processors/pods/pod_list_processor.go +++ b/cluster-autoscaler/processors/pods/pod_list_processor.go @@ -48,3 +48,32 @@ func (p *NoOpPodListProcessor) Process( // CleanUp cleans up the processor's internal structures. func (p *NoOpPodListProcessor) CleanUp() { } + +// ListedPodListProcessor is a list of PodListProcessors +type ListedPodListProcessor struct { + Processors []PodListProcessor +} + +// Process runs sub-processors sequentially +func (p *ListedPodListProcessor) Process(ctx *context.AutoscalingContext, unschedulablePods []*apiv1.Pod) ([]*apiv1.Pod, error) { + var err error + for _, processor := range p.Processors { + unschedulablePods, err = processor.Process(ctx, unschedulablePods) + if err != nil { + return nil, err + } + } + return unschedulablePods, nil +} + +// CleanUp cleans up the processor's internal structures. +func (p *ListedPodListProcessor) CleanUp() { + for _, processor := range p.Processors { + processor.CleanUp() + } +} + +// AppendProcessor append processor to the list. +func (p *ListedPodListProcessor) AppendProcessor(processor PodListProcessor) { + p.Processors = append(p.Processors, processor) +} diff --git a/cluster-autoscaler/processors/provreq/provisioning_request_processors.go b/cluster-autoscaler/processors/provreq/provisioning_request_processors.go new file mode 100644 index 000000000000..d7bee0fa63e1 --- /dev/null +++ b/cluster-autoscaler/processors/provreq/provisioning_request_processors.go @@ -0,0 +1,106 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provreq + +import ( + "fmt" + "time" + + apiv1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/autoscaler/cluster-autoscaler/context" + "k8s.io/autoscaler/cluster-autoscaler/processors/pods" + "k8s.io/autoscaler/cluster-autoscaler/utils/klogx" +) + +const ( + provisioningRequestPodAnnotationKey = "cluster-autoscaler.kubernetes.io/consume-provisioning-request" + maxProvReqEvent = 50 +) + +// EventManager is an interface for handling events for provisioning request. +type EventManager interface { + LogIgnoredInScaleUpEvent(context *context.AutoscalingContext, now time.Time, pod *apiv1.Pod, prName string) + Reset() +} + +type defaultEventManager struct { + loggedEvents int + limit int +} + +// NewDefautlEventManager return basic event manager. +func NewDefautlEventManager() *defaultEventManager { + return &defaultEventManager{limit: maxProvReqEvent} +} + +// LogIgnoredInScaleUpEvent adds event about ignored scale up for unscheduled pod, that consumes Provisioning Request. +func (e *defaultEventManager) LogIgnoredInScaleUpEvent(context *context.AutoscalingContext, now time.Time, pod *apiv1.Pod, prName string) { + message := fmt.Sprintf("Unschedulable pod ignored in scale-up loop, because it's consuming ProvisioningRequest %s/%s", pod.Namespace, prName) + if e.loggedEvents < e.limit { + context.Recorder.Event(pod, apiv1.EventTypeNormal, "", message) + e.loggedEvents++ + } +} + +// Reset resets event manager internal structure. It will be called once before handling all pods. +func (e *defaultEventManager) Reset() { + e.loggedEvents = 0 +} + +// ProvisioningRequestPodsFilter filter out pods that consumes Provisioning Request +type ProvisioningRequestPodsFilter struct { + eventManager EventManager +} + +// Process filters out all pods that are consuming a Provisioning Request from unschedulable pods list. +func (p *ProvisioningRequestPodsFilter) Process( + context *context.AutoscalingContext, + unschedulablePods []*apiv1.Pod, +) ([]*apiv1.Pod, error) { + now := time.Now() + p.eventManager.Reset() + loggingQuota := klogx.PodsLoggingQuota() + result := make([]*apiv1.Pod, 0, len(unschedulablePods)) + for _, pod := range unschedulablePods { + prName, found := provisioningRequestName(pod) + if !found { + result = append(result, pod) + continue + } + klogx.V(1).UpTo(loggingQuota).Infof("Ignoring unschedulable pod %s/%s as it consumes ProvisioningRequest: %s/%s", pod.Namespace, pod.Name, pod.Namespace, prName) + p.eventManager.LogIgnoredInScaleUpEvent(context, now, pod, prName) + } + klogx.V(1).Over(loggingQuota).Infof("There are also %v other pods which were ignored", -loggingQuota.Left()) + return result, nil +} + +// CleanUp cleans up the processor's internal structures. +func (p *ProvisioningRequestPodsFilter) CleanUp() {} + +// NewProvisioningRequestPodsFilter creates a ProvisioningRequest filter processor. +func NewProvisioningRequestPodsFilter(e EventManager) pods.PodListProcessor { + return &ProvisioningRequestPodsFilter{e} +} + +func provisioningRequestName(pod *v1.Pod) (string, bool) { + if pod == nil || pod.Annotations == nil { + return "", false + } + provReqName, found := pod.Annotations[provisioningRequestPodAnnotationKey] + return provReqName, found +} diff --git a/cluster-autoscaler/processors/provreq/provisioning_request_processors_test.go b/cluster-autoscaler/processors/provreq/provisioning_request_processors_test.go new file mode 100644 index 000000000000..29262d94bac0 --- /dev/null +++ b/cluster-autoscaler/processors/provreq/provisioning_request_processors_test.go @@ -0,0 +1,58 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package provreq + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + apiv1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/autoscaler/cluster-autoscaler/context" + . "k8s.io/autoscaler/cluster-autoscaler/utils/test" + "k8s.io/client-go/tools/record" +) + +func TestProvisioningRequestPodsFilter(t *testing.T) { + prPod := BuildTestPod("pr-pod", 500, 10) + prPod.Annotations[provisioningRequestPodAnnotationKey] = "provisioning-request-class" + pod := BuildTestPod("pod", 500, 10) + testCases := map[string]struct { + unschedulableCandidates []*apiv1.Pod + expectedUnscheduledPods []*apiv1.Pod + }{ + "ProvisioningRequest consumer is filtered out": { + unschedulableCandidates: []*v1.Pod{prPod, pod}, + expectedUnscheduledPods: []*v1.Pod{pod}, + }, + } + for _, test := range testCases { + eventRecorder := record.NewFakeRecorder(10) + ctx := &context.AutoscalingContext{AutoscalingKubeClients: context.AutoscalingKubeClients{Recorder: eventRecorder}} + filter := NewProvisioningRequestPodsFilter(NewDefautlEventManager()) + got, _ := filter.Process(ctx, test.unschedulableCandidates) + assert.ElementsMatch(t, got, test.expectedUnscheduledPods) + select { + case event := <-eventRecorder.Events: + assert.Contains(t, event, "Unschedulable pod ignored in scale-up loop") + case <-time.After(1 * time.Second): + t.Errorf("Timeout waiting for event") + } + + } +} diff --git a/cluster-autoscaler/provisioningrequest/service/v1beta1client/client.go b/cluster-autoscaler/provisioningrequest/provreqclient/client.go similarity index 68% rename from cluster-autoscaler/provisioningrequest/service/v1beta1client/client.go rename to cluster-autoscaler/provisioningrequest/provreqclient/client.go index 59b534ac2be5..db1852b35c51 100644 --- a/cluster-autoscaler/provisioningrequest/service/v1beta1client/client.go +++ b/cluster-autoscaler/provisioningrequest/provreqclient/client.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1client +package provreqclient import ( "fmt" @@ -28,6 +28,7 @@ import ( "k8s.io/autoscaler/cluster-autoscaler/provisioningrequest/client/clientset/versioned" "k8s.io/autoscaler/cluster-autoscaler/provisioningrequest/client/informers/externalversions" listers "k8s.io/autoscaler/cluster-autoscaler/provisioningrequest/client/listers/autoscaling.x-k8s.io/v1beta1" + "k8s.io/autoscaler/cluster-autoscaler/provisioningrequest/provreqwrapper" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" v1 "k8s.io/client-go/listers/core/v1" @@ -40,15 +41,22 @@ const ( provisioningRequestClientCallTimeout = 4 * time.Second ) -// ProvisioningRequestClient represents client for v1beta1 ProvReq CRD. -type ProvisioningRequestClient struct { +// ProvisioningRequestClient represents the service that is able to list +// and access different Provisioning Requests. +type ProvisioningRequestClient interface { + ProvisioningRequest(namespace, name string) (*provreqwrapper.ProvisioningRequest, error) + ProvisioningRequests() ([]*provreqwrapper.ProvisioningRequest, error) +} + +// ProvisioningRequestClientV1beta1 represents client for v1beta1 ProvReq CRD. +type ProvisioningRequestClientV1beta1 struct { client versioned.Interface provReqLister listers.ProvisioningRequestLister podTemplLister v1.PodTemplateLister } // NewProvisioningRequestClient configures and returns a provisioningRequestClient. -func NewProvisioningRequestClient(kubeConfig *rest.Config) (*ProvisioningRequestClient, error) { +func NewProvisioningRequestClient(kubeConfig *rest.Config) (ProvisioningRequestClient, error) { prClient, err := newPRClient(kubeConfig) if err != nil { return nil, fmt.Errorf("Failed to create Provisioning Request client: %v", err) @@ -69,7 +77,7 @@ func NewProvisioningRequestClient(kubeConfig *rest.Config) (*ProvisioningRequest return nil, err } - return &ProvisioningRequestClient{ + return &ProvisioningRequestClientV1beta1{ client: prClient, provReqLister: provReqLister, podTemplLister: podTemplLister, @@ -77,21 +85,37 @@ func NewProvisioningRequestClient(kubeConfig *rest.Config) (*ProvisioningRequest } // ProvisioningRequest gets a specific ProvisioningRequest CR. -func (c *ProvisioningRequestClient) ProvisioningRequest(namespace, name string) (*v1beta1.ProvisioningRequest, error) { - return c.provReqLister.ProvisioningRequests(namespace).Get(name) +func (c *ProvisioningRequestClientV1beta1) ProvisioningRequest(namespace, name string) (*provreqwrapper.ProvisioningRequest, error) { + v1Beta1PR, err := c.provReqLister.ProvisioningRequests(namespace).Get(name) + if err != nil { + return nil, err + } + podTemplates, err := c.FetchPodTemplates(v1Beta1PR) + if err != nil { + return nil, fmt.Errorf("while fetching pod templates for Get Provisioning Request %s/%s got error: %v", namespace, name, err) + } + return provreqwrapper.NewV1Beta1ProvisioningRequest(v1Beta1PR, podTemplates), nil } // ProvisioningRequests gets all ProvisioningRequest CRs. -func (c *ProvisioningRequestClient) ProvisioningRequests() ([]*v1beta1.ProvisioningRequest, error) { - provisioningRequests, err := c.provReqLister.List(labels.Everything()) +func (c *ProvisioningRequestClientV1beta1) ProvisioningRequests() ([]*provreqwrapper.ProvisioningRequest, error) { + v1Beta1PRs, err := c.provReqLister.List(labels.Everything()) if err != nil { return nil, fmt.Errorf("error fetching provisioningRequests: %w", err) } - return provisioningRequests, nil + prs := make([]*provreqwrapper.ProvisioningRequest, 0, len(v1Beta1PRs)) + for _, v1Beta1PR := range v1Beta1PRs { + podTemplates, errPodTemplates := c.FetchPodTemplates(v1Beta1PR) + if errPodTemplates != nil { + return nil, fmt.Errorf("while fetching pod templates for List Provisioning Request %s/%s got error: %v", v1Beta1PR.Namespace, v1Beta1PR.Name, errPodTemplates) + } + prs = append(prs, provreqwrapper.NewV1Beta1ProvisioningRequest(v1Beta1PR, podTemplates)) + } + return prs, nil } // FetchPodTemplates fetches PodTemplates referenced by the Provisioning Request. -func (c *ProvisioningRequestClient) FetchPodTemplates(pr *v1beta1.ProvisioningRequest) ([]*apiv1.PodTemplate, error) { +func (c *ProvisioningRequestClientV1beta1) FetchPodTemplates(pr *v1beta1.ProvisioningRequest) ([]*apiv1.PodTemplate, error) { podTemplates := make([]*apiv1.PodTemplate, 0, len(pr.Spec.PodSets)) for _, podSpec := range pr.Spec.PodSets { podTemplate, err := c.podTemplLister.PodTemplates(pr.Namespace).Get(podSpec.PodTemplateRef.Name) diff --git a/cluster-autoscaler/provisioningrequest/service/v1beta1client/client_test.go b/cluster-autoscaler/provisioningrequest/provreqclient/client_test.go similarity index 98% rename from cluster-autoscaler/provisioningrequest/service/v1beta1client/client_test.go rename to cluster-autoscaler/provisioningrequest/provreqclient/client_test.go index 7d5e25ce1e38..c05bb490f2f2 100644 --- a/cluster-autoscaler/provisioningrequest/service/v1beta1client/client_test.go +++ b/cluster-autoscaler/provisioningrequest/provreqclient/client_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1client +package provreqclient import ( "context" diff --git a/cluster-autoscaler/provisioningrequest/service/v1beta1client/testutils.go b/cluster-autoscaler/provisioningrequest/provreqclient/testutils.go similarity index 97% rename from cluster-autoscaler/provisioningrequest/service/v1beta1client/testutils.go rename to cluster-autoscaler/provisioningrequest/provreqclient/testutils.go index 25fe65e6e941..578c790b7359 100644 --- a/cluster-autoscaler/provisioningrequest/service/v1beta1client/testutils.go +++ b/cluster-autoscaler/provisioningrequest/provreqclient/testutils.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1client +package provreqclient import ( "context" @@ -35,7 +35,7 @@ import ( ) // NewFakeProvisioningRequestClient mock ProvisioningRequestClient for tests. -func NewFakeProvisioningRequestClient(ctx context.Context, t *testing.T, prs ...*provreqwrapper.ProvisioningRequest) (*ProvisioningRequestClient, *FakeProvisioningRequestForceClient) { +func NewFakeProvisioningRequestClient(ctx context.Context, t *testing.T, prs ...*provreqwrapper.ProvisioningRequest) (*ProvisioningRequestClientV1beta1, *FakeProvisioningRequestForceClient) { t.Helper() provReqClient := fake.NewSimpleClientset() podTemplClient := fake_kubernetes.NewSimpleClientset() @@ -60,7 +60,7 @@ func NewFakeProvisioningRequestClient(ctx context.Context, t *testing.T, prs ... if err != nil { t.Fatalf("Failed to create Provisioning Request lister. Error was: %v", err) } - return &ProvisioningRequestClient{ + return &ProvisioningRequestClientV1beta1{ client: provReqClient, provReqLister: provReqLister, podTemplLister: podTemplLister, diff --git a/cluster-autoscaler/provisioningrequest/service/service.go b/cluster-autoscaler/provisioningrequest/service/service.go deleted file mode 100644 index 72ffe9e1a43f..000000000000 --- a/cluster-autoscaler/provisioningrequest/service/service.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright 2023 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package provreqservice - -import ( - "fmt" - - "k8s.io/autoscaler/cluster-autoscaler/provisioningrequest/provreqwrapper" - "k8s.io/autoscaler/cluster-autoscaler/provisioningrequest/service/v1beta1client" - "k8s.io/client-go/rest" -) - -// ProvisioningRequestService represents the service that is able to list, -// access and delete different Provisioning Requests. -type ProvisioningRequestService struct { - provReqV1Beta1Client *v1beta1client.ProvisioningRequestClient -} - -// NewProvisioningRequestService returns new service for interacting with ProvisioningRequests. -func NewProvisioningRequestService(kubeConfig *rest.Config) (*ProvisioningRequestService, error) { - v1Beta1Client, err := v1beta1client.NewProvisioningRequestClient(kubeConfig) - if err != nil { - return nil, err - } - return &ProvisioningRequestService{ - provReqV1Beta1Client: v1Beta1Client, - }, nil -} - -// ProvisioningRequest gets a specific ProvisioningRequest CR. -func (s *ProvisioningRequestService) ProvisioningRequest(namespace, name string) (*provreqwrapper.ProvisioningRequest, error) { - v1Beta1PR, err := s.provReqV1Beta1Client.ProvisioningRequest(namespace, name) - if err == nil { - podTemplates, errPodTemplates := s.provReqV1Beta1Client.FetchPodTemplates(v1Beta1PR) - if errPodTemplates != nil { - return nil, fmt.Errorf("while fetching pod templates for Get Provisioning Request %s/%s got error: %v", namespace, name, errPodTemplates) - } - return provreqwrapper.NewV1Beta1ProvisioningRequest(v1Beta1PR, podTemplates), nil - } - return nil, err -} - -// ProvisioningRequests gets all Queued ProvisioningRequest CRs. -func (s *ProvisioningRequestService) ProvisioningRequests() ([]*provreqwrapper.ProvisioningRequest, error) { - v1Beta1PRs, err := s.provReqV1Beta1Client.ProvisioningRequests() - if err != nil { - return nil, err - } - prs := make([]*provreqwrapper.ProvisioningRequest, 0, len(v1Beta1PRs)) - for _, v1Beta1PR := range v1Beta1PRs { - podTemplates, errPodTemplates := s.provReqV1Beta1Client.FetchPodTemplates(v1Beta1PR) - if errPodTemplates != nil { - return nil, fmt.Errorf("while fetching pod templates for List Provisioning Request %s/%s got error: %v", v1Beta1PR.Namespace, v1Beta1PR.Name, errPodTemplates) - } - prs = append(prs, provreqwrapper.NewV1Beta1ProvisioningRequest(v1Beta1PR, podTemplates)) - } - return prs, nil -}