-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6299 from yaroslava-serdiuk/provreq
Implement ProvisioningRequest service
- Loading branch information
Showing
6 changed files
with
686 additions
and
0 deletions.
There are no files selected for viewing
137 changes: 137 additions & 0 deletions
137
cluster-autoscaler/provisioningrequest/provreqwrapper/wrapper.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
/* | ||
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 provreqwrapper | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
apiv1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/autoscaler/cluster-autoscaler/provisioningrequest/apis/autoscaling.x-k8s.io/v1beta1" | ||
) | ||
|
||
// ProvisioningRequest wrapper representation of the ProvisisoningRequest | ||
type ProvisioningRequest struct { | ||
v1Beta1PR *v1beta1.ProvisioningRequest | ||
v1Beta1PodTemplates []*apiv1.PodTemplate | ||
} | ||
|
||
// PodSet wrapper representation of the PodSet. | ||
type PodSet struct { | ||
// Count number of pods with given template. | ||
Count int32 | ||
// PodTemplate template of given pod set. | ||
PodTemplate apiv1.PodTemplateSpec | ||
} | ||
|
||
// NewV1Beta1ProvisioningRequest creates new ProvisioningRequest based on v1beta1 CR. | ||
func NewV1Beta1ProvisioningRequest(v1Beta1PR *v1beta1.ProvisioningRequest, v1Beta1PodTemplates []*apiv1.PodTemplate) *ProvisioningRequest { | ||
return &ProvisioningRequest{ | ||
v1Beta1PR: v1Beta1PR, | ||
v1Beta1PodTemplates: v1Beta1PodTemplates, | ||
} | ||
} | ||
|
||
// Name of the Provisioning Request. | ||
func (pr *ProvisioningRequest) Name() string { | ||
return pr.v1Beta1PR.Name | ||
} | ||
|
||
// Namespace of the Provisioning Request. | ||
func (pr *ProvisioningRequest) Namespace() string { | ||
return pr.v1Beta1PR.Namespace | ||
} | ||
|
||
// CreationTimestamp of the Provisioning Request. | ||
func (pr *ProvisioningRequest) CreationTimestamp() metav1.Time { | ||
return pr.v1Beta1PR.CreationTimestamp | ||
} | ||
|
||
// RuntimeObject returns runtime.Object of the Provisioning Request. | ||
func (pr *ProvisioningRequest) RuntimeObject() runtime.Object { | ||
return pr.v1Beta1PR | ||
} | ||
|
||
// APIVersion returns APIVersion of the Provisioning Request. | ||
func (pr *ProvisioningRequest) APIVersion() string { | ||
return pr.v1Beta1PR.APIVersion | ||
} | ||
|
||
// Kind returns Kind of the Provisioning Request. | ||
func (pr *ProvisioningRequest) Kind() string { | ||
return pr.v1Beta1PR.Kind | ||
|
||
} | ||
|
||
// UID returns UID of the Provisioning Request. | ||
func (pr *ProvisioningRequest) UID() types.UID { | ||
return pr.v1Beta1PR.UID | ||
} | ||
|
||
// Conditions of the Provisioning Request. | ||
func (pr *ProvisioningRequest) Conditions() []metav1.Condition { | ||
return pr.v1Beta1PR.Status.Conditions | ||
} | ||
|
||
// SetConditions of the Provisioning Request. | ||
func (pr *ProvisioningRequest) SetConditions(conditions []metav1.Condition) { | ||
pr.v1Beta1PR.Status.Conditions = conditions | ||
return | ||
} | ||
|
||
// PodSets of the Provisioning Request. | ||
func (pr *ProvisioningRequest) PodSets() ([]PodSet, error) { | ||
if len(pr.v1Beta1PR.Spec.PodSets) != len(pr.v1Beta1PodTemplates) { | ||
return nil, errMissingPodTemplates(pr.v1Beta1PR.Spec.PodSets, pr.v1Beta1PodTemplates) | ||
} | ||
podSets := make([]PodSet, 0, len(pr.v1Beta1PR.Spec.PodSets)) | ||
for i, podSet := range pr.v1Beta1PR.Spec.PodSets { | ||
podSets = append(podSets, PodSet{ | ||
Count: podSet.Count, | ||
PodTemplate: pr.v1Beta1PodTemplates[i].Template, | ||
}) | ||
} | ||
return podSets, nil | ||
} | ||
|
||
// V1Beta1 returns v1beta1 object CR, to be used only to pass information to clients. | ||
func (pr *ProvisioningRequest) V1Beta1() *v1beta1.ProvisioningRequest { | ||
return pr.v1Beta1PR | ||
} | ||
|
||
// PodTemplates returns pod templates associated with the Provisioning Request, to be used only to pass information to clients. | ||
func (pr *ProvisioningRequest) PodTemplates() []*apiv1.PodTemplate { | ||
return pr.v1Beta1PodTemplates | ||
} | ||
|
||
// errMissingPodTemplates creates error that is passed when there are missing pod templates. | ||
func errMissingPodTemplates(podSets []v1beta1.PodSet, podTemplates []*apiv1.PodTemplate) error { | ||
foundPodTemplates := map[string]struct{}{} | ||
for _, pt := range podTemplates { | ||
foundPodTemplates[pt.Name] = struct{}{} | ||
} | ||
missingTemplates := make([]string, 0) | ||
for _, ps := range podSets { | ||
if _, found := foundPodTemplates[ps.PodTemplateRef.Name]; !found { | ||
missingTemplates = append(missingTemplates, ps.PodTemplateRef.Name) | ||
} | ||
} | ||
return fmt.Errorf("missing pod templates, %d pod templates were referenced, %d templates were missing: %s", len(podSets), len(missingTemplates), strings.Join(missingTemplates, ",")) | ||
} |
142 changes: 142 additions & 0 deletions
142
cluster-autoscaler/provisioningrequest/provreqwrapper/wrapper_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/* | ||
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 provreqwrapper | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
apiv1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/autoscaler/cluster-autoscaler/provisioningrequest/apis/autoscaling.x-k8s.io/v1beta1" | ||
) | ||
|
||
func TestProvisioningRequestWrapper(t *testing.T) { | ||
creationTimestamp := metav1.NewTime(time.Date(2023, 11, 12, 13, 14, 15, 0, time.UTC)) | ||
conditions := []metav1.Condition{ | ||
{ | ||
LastTransitionTime: metav1.NewTime(time.Date(2022, 11, 12, 13, 14, 15, 0, time.UTC)), | ||
Message: "Message", | ||
ObservedGeneration: 1, | ||
Reason: "Reason", | ||
Status: "Status", | ||
Type: "ConditionType", | ||
}, | ||
} | ||
podSets := []PodSet{ | ||
{ | ||
Count: 1, | ||
PodTemplate: apiv1.PodTemplateSpec{ | ||
Spec: apiv1.PodSpec{ | ||
Containers: []apiv1.Container{ | ||
{ | ||
Name: "test-container", | ||
Image: "test-image", | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
podTemplates := []*apiv1.PodTemplate{ | ||
{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "name-pod-template-beta", | ||
Namespace: "namespace-beta", | ||
CreationTimestamp: creationTimestamp, | ||
}, | ||
Template: apiv1.PodTemplateSpec{ | ||
Spec: apiv1.PodSpec{ | ||
Containers: []apiv1.Container{ | ||
{ | ||
Name: "test-container", | ||
Image: "test-image", | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
v1Beta1PR := &v1beta1.ProvisioningRequest{ | ||
TypeMeta: metav1.TypeMeta{ | ||
APIVersion: "beta-api", | ||
Kind: "beta-kind", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "name-beta", | ||
Namespace: "namespace-beta", | ||
CreationTimestamp: creationTimestamp, | ||
UID: types.UID("beta-uid"), | ||
}, | ||
Spec: v1beta1.ProvisioningRequestSpec{ | ||
ProvisioningClassName: "queued-provisioning.gke.io", | ||
PodSets: []v1beta1.PodSet{ | ||
{ | ||
Count: 1, | ||
PodTemplateRef: v1beta1.Reference{ | ||
Name: podTemplates[0].Name, | ||
}, | ||
}, | ||
}, | ||
}, | ||
Status: v1beta1.ProvisioningRequestStatus{ | ||
Conditions: conditions, | ||
ProvisioningClassDetails: map[string]v1beta1.Detail{}, | ||
}, | ||
} | ||
|
||
wrappedBetaPR := NewV1Beta1ProvisioningRequest(v1Beta1PR, podTemplates) | ||
|
||
// Check Name, Namespace and Creation accessors | ||
assert.Equal(t, "name-beta", wrappedBetaPR.Name()) | ||
assert.Equal(t, "namespace-beta", wrappedBetaPR.Namespace()) | ||
assert.Equal(t, creationTimestamp, wrappedBetaPR.CreationTimestamp()) | ||
|
||
// Check APIVersion, Kind and UID accessors | ||
assert.Equal(t, "beta-api", wrappedBetaPR.APIVersion()) | ||
assert.Equal(t, "beta-kind", wrappedBetaPR.Kind()) | ||
assert.Equal(t, types.UID("beta-uid"), wrappedBetaPR.UID()) | ||
|
||
// Check the initial conditions | ||
assert.Equal(t, conditions, wrappedBetaPR.Conditions()) | ||
|
||
// Clear conditions and check the values | ||
wrappedBetaPR.SetConditions(nil) | ||
assert.Nil(t, wrappedBetaPR.Conditions()) | ||
|
||
// Set conditions and check the values | ||
wrappedBetaPR.SetConditions(conditions) | ||
assert.Equal(t, conditions, wrappedBetaPR.Conditions()) | ||
|
||
// Check the PodSets | ||
betaPodSets, betaErr := wrappedBetaPR.PodSets() | ||
assert.Nil(t, betaErr) | ||
assert.Equal(t, podSets, betaPodSets) | ||
|
||
// Check the type accessors. | ||
assert.Equal(t, v1Beta1PR, wrappedBetaPR.V1Beta1()) | ||
assert.Equal(t, podTemplates, wrappedBetaPR.PodTemplates()) | ||
|
||
// Check case where the Provisioning Request is missing Pod Templates. | ||
wrappedBetaPRMissingPodTemplates := NewV1Beta1ProvisioningRequest(v1Beta1PR, nil) | ||
podSets, err := wrappedBetaPRMissingPodTemplates.PodSets() | ||
assert.Nil(t, podSets) | ||
assert.EqualError(t, err, "missing pod templates, 1 pod templates were referenced, 1 templates were missing: name-pod-template-beta") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
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 | ||
} |
Oops, something went wrong.