Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Add Conversion webhooks for v0.34.x #1492

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions kwok/cloudprovider/cloudprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,20 @@ import (
"math/rand"
"strings"

"github.com/awslabs/operatorpkg/object"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/samber/lo"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

"sigs.k8s.io/karpenter/pkg/apis/v1beta1"
"sigs.k8s.io/karpenter/pkg/cloudprovider"
"sigs.k8s.io/karpenter/pkg/scheduling"
"sigs.k8s.io/karpenter/pkg/test/v1alpha1"
)

func NewCloudProvider(ctx context.Context, kubeClient client.Client, instanceTypes []*cloudprovider.InstanceType) *CloudProvider {
Expand Down Expand Up @@ -118,6 +121,16 @@ func (c CloudProvider) Name() string {
return "kwok"
}

func (c *CloudProvider) GetSupportedNodeClasses() []schema.GroupVersionKind {
return []schema.GroupVersionKind{
{
Group: object.GVK(&v1alpha1.TestNodeClass{}).Group,
Version: object.GVK(&v1alpha1.TestNodeClass{}).Version,
Kind: object.GVK(&v1alpha1.TestNodeClass{}).Kind,
},
}
}

func (c CloudProvider) getInstanceType(instanceTypeName string) (*cloudprovider.InstanceType, error) {
it, found := lo.Find(c.instanceTypes, func(it *cloudprovider.InstanceType) bool {
return it.Name == instanceTypeName
Expand Down
2 changes: 1 addition & 1 deletion kwok/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ func main() {
state.NewCluster(op.Clock, op.GetClient(), cloudProvider),
op.EventRecorder,
cloudProvider,
)...).Start(ctx)
)...).Start(ctx, cloudProvider)
}
186 changes: 186 additions & 0 deletions pkg/apis/v1/nodeclaim_conversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
Copyright 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 v1

import (
"context"
"encoding/json"
"fmt"

"github.com/awslabs/operatorpkg/status"
"github.com/samber/lo"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"knative.dev/pkg/apis"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"sigs.k8s.io/karpenter/pkg/apis/v1beta1"
"sigs.k8s.io/karpenter/pkg/operator/injection"
)

// convert v1 to v1beta1
func (in *NodeClaim) ConvertTo(ctx context.Context, to apis.Convertible) error {
v1beta1NC := to.(*v1beta1.NodeClaim)
v1beta1NC.ObjectMeta = in.ObjectMeta

in.Status.convertTo((&v1beta1NC.Status))
return in.Spec.convertTo(ctx, &v1beta1NC.Spec, in.Annotations[KubeletCompatibilityAnnotationKey])
}

func (in *NodeClaimSpec) convertTo(ctx context.Context, v1beta1nc *v1beta1.NodeClaimSpec, kubeletAnnotation string) error {
v1beta1nc.Taints = in.Taints
v1beta1nc.StartupTaints = in.StartupTaints
v1beta1nc.Resources = v1beta1.ResourceRequirements(in.Resources)
v1beta1nc.Requirements = lo.Map(in.Requirements, func(v1Requirements NodeSelectorRequirementWithMinValues, _ int) v1.NodeSelectorRequirement {
return v1.NodeSelectorRequirement{
Key: v1Requirements.Key,
Operator: v1Requirements.Operator,
Values: v1Requirements.Values,
}
})

if in.NodeClassRef != nil {
nodeclass, found := lo.Find(injection.GetNodeClasses(ctx), func(nc schema.GroupVersionKind) bool {
return nc.Kind == in.NodeClassRef.Kind && nc.Group == in.NodeClassRef.Group
})
v1beta1nc.NodeClassRef = &v1beta1.NodeClassReference{
Kind: in.NodeClassRef.Kind,
Name: in.NodeClassRef.Name,
APIVersion: lo.Ternary(found, nodeclass.GroupVersion().String(), ""),
}
}

if kubeletAnnotation != "" {
v1beta1kubelet := &v1beta1.KubeletConfiguration{}
err := json.Unmarshal([]byte(kubeletAnnotation), v1beta1kubelet)
if err != nil {
return fmt.Errorf("unmarshaling kubelet config annotation, %w", err)
}
v1beta1nc.Kubelet = v1beta1kubelet
}
return nil
}

func (in *NodeClaimStatus) convertTo(v1beta1nc *v1beta1.NodeClaimStatus) {
v1beta1nc.NodeName = in.NodeName
v1beta1nc.ProviderID = in.ProviderID
v1beta1nc.ImageID = in.ImageID
v1beta1nc.Capacity = in.Capacity
v1beta1nc.Allocatable = in.Allocatable
v1beta1nc.Conditions = lo.Map(in.Conditions, func(v1status status.Condition, _ int) apis.Condition {
return apis.Condition{
Type: apis.ConditionType(v1status.Type),
Reason: v1status.Reason,
Status: v1.ConditionStatus(v1status.Status),
Message: v1status.Message,
LastTransitionTime: apis.VolatileTime{Inner: v1status.LastTransitionTime},
}
})
}

// convert v1beta1 to v1
func (in *NodeClaim) ConvertFrom(ctx context.Context, from apis.Convertible) error {
v1beta1NC := from.(*v1beta1.NodeClaim)
in.ObjectMeta = v1beta1NC.ObjectMeta

in.Status.convertFrom((&v1beta1NC.Status))
kubeletAnnotation, err := in.Spec.convertFrom(ctx, &v1beta1NC.Spec)
if err != nil {
return err
}
if kubeletAnnotation == "" {
in.Annotations = lo.OmitByKeys(in.Annotations, []string{KubeletCompatibilityAnnotationKey})
} else {
in.Annotations = lo.Assign(in.Annotations, map[string]string{KubeletCompatibilityAnnotationKey: kubeletAnnotation})
}
return in.setExpireAfter(ctx, v1beta1NC)
}

// only need to set expireAfter for v1beta1 to v1
func (in *NodeClaim) setExpireAfter(ctx context.Context, v1beta1nc *v1beta1.NodeClaim) error {
kubeClient := injection.GetClient(ctx)
nodePoolName, ok := v1beta1nc.Labels[NodePoolLabelKey]
if !ok {
// If we don't have a nodepool for this nodeclaim, there's nothing to look up
return nil
}
nodePool := &NodePool{}
if err := kubeClient.Get(ctx, types.NamespacedName{Name: nodePoolName}, nodePool); err != nil {
if errors.IsNotFound(err) {
// If the nodepool doesn't exist, fallback to no expiry, and use the CRD default
return nil
}
return fmt.Errorf("getting nodepool, %w", err)
}
in.Spec.ExpireAfter = nodePool.Spec.Template.Spec.ExpireAfter
return nil
}

func (in *NodeClaimSpec) convertFrom(ctx context.Context, v1beta1nc *v1beta1.NodeClaimSpec) (string, error) {
in.Taints = v1beta1nc.Taints
in.StartupTaints = v1beta1nc.StartupTaints
in.Resources = ResourceRequirements(v1beta1nc.Resources)
in.Requirements = lo.Map(v1beta1nc.Requirements, func(v1beta1Requirements v1.NodeSelectorRequirement, _ int) NodeSelectorRequirementWithMinValues {
return NodeSelectorRequirementWithMinValues{
NodeSelectorRequirement: v1.NodeSelectorRequirement{
Key: v1beta1Requirements.Key,
Operator: v1beta1Requirements.Operator,
Values: v1beta1Requirements.Values,
},
}
})

defaultNodeClassGVK := injection.GetNodeClasses(ctx)[0]
nodeclassGroupVersion, err := schema.ParseGroupVersion(v1beta1nc.NodeClassRef.APIVersion)
if err != nil {
return "", err
}
in.NodeClassRef = &NodeClassReference{
Name: v1beta1nc.NodeClassRef.Name,
Kind: lo.Ternary(v1beta1nc.NodeClassRef.Kind == "", defaultNodeClassGVK.Kind, v1beta1nc.NodeClassRef.Kind),
Group: lo.Ternary(v1beta1nc.NodeClassRef.APIVersion == "", defaultNodeClassGVK.Group, nodeclassGroupVersion.Group),
}

if v1beta1nc.Kubelet != nil {
kubelet, err := json.Marshal(v1beta1nc.Kubelet)
if err != nil {
return "", fmt.Errorf("marshaling kubelet config annotation, %w", err)
}
return string(kubelet), nil
}
return "", nil
}

func (in *NodeClaimStatus) convertFrom(v1beta1nc *v1beta1.NodeClaimStatus) {
in.NodeName = v1beta1nc.NodeName
in.ProviderID = v1beta1nc.ProviderID
in.ImageID = v1beta1nc.ImageID
in.Capacity = v1beta1nc.Capacity
in.Allocatable = v1beta1nc.Allocatable
in.Conditions = lo.Map(v1beta1nc.Conditions, func(v1beta1status apis.Condition, _ int) status.Condition {
return status.Condition{
Type: string(v1beta1status.Type),
Reason: v1beta1status.Reason,
Status: metav1.ConditionStatus(v1beta1status.Status),
Message: v1beta1status.Message,
LastTransitionTime: v1beta1status.LastTransitionTime.Inner,
}
})
}
Loading
Loading