Skip to content

Commit 2cf5629

Browse files
committed
feat: Add kubernetes.azure.com/scalesetpriority to WellKnownLabels
1 parent 93fc7ee commit 2cf5629

File tree

11 files changed

+143
-2
lines changed

11 files changed

+143
-2
lines changed

pkg/apis/v1beta1/labels.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ var (
6565

6666
AKSLabelCluster,
6767
AKSLabelMode,
68+
AKSLabelScaleSetPriority,
6869
)
6970

7071
RestrictedLabels = sets.New(
@@ -109,7 +110,8 @@ var (
109110

110111
AKSLabelCluster = AKSLabelDomain + "/cluster"
111112
AKSLabelKubeletIdentityClientID = AKSLabelDomain + "/kubelet-identity-client-id"
112-
AKSLabelMode = AKSLabelDomain + "/mode" // "system" or "user"
113+
AKSLabelMode = AKSLabelDomain + "/mode" // "system" or "user"
114+
AKSLabelScaleSetPriority = AKSLabelDomain + "/scalesetpriority" // "spot" or "regular". Note that "regular" is never written by AKS as a label but we write it to make scheduling easier
113115

114116
AnnotationAKSNodeClassHash = apis.Group + "/aksnodeclass-hash"
115117
AnnotationAKSNodeClassHashVersion = apis.Group + "/aksnodeclass-hash-version"
@@ -127,6 +129,11 @@ const (
127129
AzureLinuxImageFamily = "AzureLinux"
128130
)
129131

132+
const (
133+
ScaleSetPriorityRegular = "regular"
134+
ScaleSetPrioritySpot = "spot"
135+
)
136+
130137
var UbuntuFamilies = sets.New(
131138
UbuntuImageFamily,
132139
Ubuntu2204ImageFamily,

pkg/cloudprovider/cloudprovider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ func (c *CloudProvider) vmInstanceToNodeClaim(ctx context.Context, vm *armcomput
488488
}
489489

490490
labels[karpv1.CapacityTypeLabelKey] = instance.GetCapacityTypeFromVM(vm)
491+
labels[v1beta1.AKSLabelScaleSetPriority] = instance.GetScaleSetPriorityLabelFromVM(vm)
491492

492493
if tag, ok := vm.Tags[launchtemplate.NodePoolTagKey]; ok {
493494
labels[karpv1.NodePoolLabelKey] = *tag

pkg/providers/instance/offerings/offerings_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"testing"
2222

23+
"github.com/Azure/karpenter-provider-azure/pkg/apis/v1beta1"
2324
"github.com/stretchr/testify/assert"
2425
corev1 "k8s.io/api/core/v1"
2526
karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1"
@@ -89,6 +90,7 @@ func TestPickSkuSizePriorityAndZone(t *testing.T) {
8990
Price: 0.05,
9091
Requirements: scheduling.NewRequirements(
9192
scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, karpv1.CapacityTypeSpot),
93+
scheduling.NewRequirement(v1beta1.AKSLabelScaleSetPriority, corev1.NodeSelectorOpIn, v1beta1.ScaleSetPrioritySpot),
9294
scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpIn, "westus-1"),
9395
),
9496
Available: true,
@@ -97,6 +99,7 @@ func TestPickSkuSizePriorityAndZone(t *testing.T) {
9799
Price: 0.1,
98100
Requirements: scheduling.NewRequirements(
99101
scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, karpv1.CapacityTypeOnDemand),
102+
scheduling.NewRequirement(v1beta1.AKSLabelScaleSetPriority, corev1.NodeSelectorOpIn, v1beta1.ScaleSetPriorityRegular),
100103
scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpIn, "westus-1"),
101104
),
102105
Available: true,
@@ -128,6 +131,57 @@ func TestPickSkuSizePriorityAndZone(t *testing.T) {
128131
expectedPriority: karpv1.CapacityTypeSpot,
129132
expectedZone: "westus-1",
130133
},
134+
{
135+
name: "Select spot instance when requested (via legacy kubernetes.azure.com/scalesetpriority label)",
136+
instanceTypes: []*cloudprovider.InstanceType{
137+
{
138+
Name: "Standard_D2s_v3",
139+
Offerings: []*cloudprovider.Offering{
140+
{
141+
Price: 0.05,
142+
Requirements: scheduling.NewRequirements(
143+
scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, karpv1.CapacityTypeSpot),
144+
scheduling.NewRequirement(v1beta1.AKSLabelScaleSetPriority, corev1.NodeSelectorOpIn, v1beta1.ScaleSetPrioritySpot),
145+
scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpIn, "westus-1"),
146+
),
147+
Available: true,
148+
},
149+
{
150+
Price: 0.1,
151+
Requirements: scheduling.NewRequirements(
152+
scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, karpv1.CapacityTypeOnDemand),
153+
scheduling.NewRequirement(v1beta1.AKSLabelScaleSetPriority, corev1.NodeSelectorOpIn, v1beta1.ScaleSetPriorityRegular),
154+
scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpIn, "westus-1"),
155+
),
156+
Available: true,
157+
},
158+
},
159+
},
160+
},
161+
nodeClaim: &karpv1.NodeClaim{
162+
Spec: karpv1.NodeClaimSpec{
163+
Requirements: []karpv1.NodeSelectorRequirementWithMinValues{
164+
{
165+
NodeSelectorRequirement: corev1.NodeSelectorRequirement{
166+
Key: v1beta1.AKSLabelScaleSetPriority,
167+
Operator: corev1.NodeSelectorOpIn,
168+
Values: []string{v1beta1.ScaleSetPrioritySpot},
169+
},
170+
},
171+
{
172+
NodeSelectorRequirement: corev1.NodeSelectorRequirement{
173+
Key: corev1.LabelTopologyZone,
174+
Operator: corev1.NodeSelectorOpIn,
175+
Values: []string{"westus-1"},
176+
},
177+
},
178+
},
179+
},
180+
},
181+
expectedInstanceType: "Standard_D2s_v3",
182+
expectedPriority: karpv1.CapacityTypeSpot,
183+
expectedZone: "westus-1",
184+
},
131185
{
132186
name: "Multiple zones - should pick one of the available zones",
133187
instanceTypes: []*cloudprovider.InstanceType{

pkg/providers/instance/vminstance.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ var (
5858
armcompute.VirtualMachinePriorityTypesSpot: karpv1.CapacityTypeSpot,
5959
armcompute.VirtualMachinePriorityTypesRegular: karpv1.CapacityTypeOnDemand,
6060
}
61+
// Note that there is no ScaleSetPriorityToKarpCapacityType because the karpenter.sh/capacity-type
62+
// label is the "official" label that we actually key priority off of. Selection still works though
63+
// because when we list instance types on-demand offerings always have v1beta1.ScaleSetPriorityRegular
64+
// and spot instances always have v1beta1.ScaleSetPrioritySpot, so the correct karpenter.sh/capacity-type
65+
// label is still selected even if the user is using kubernetes.azure.com/scalesetpriority as the key.
66+
VMPriorityToScaleSetPriority = map[armcompute.VirtualMachinePriorityTypes]string{
67+
armcompute.VirtualMachinePriorityTypesSpot: v1beta1.ScaleSetPrioritySpot,
68+
armcompute.VirtualMachinePriorityTypesRegular: v1beta1.ScaleSetPriorityRegular,
69+
}
6170
)
6271

6372
const (
@@ -989,3 +998,10 @@ func GetCapacityTypeFromVM(vm *armcompute.VirtualMachine) string {
989998
}
990999
return ""
9911000
}
1001+
1002+
func GetScaleSetPriorityLabelFromVM(vm *armcompute.VirtualMachine) string {
1003+
if vm != nil && vm.Properties != nil && vm.Properties.Priority != nil {
1004+
return VMPriorityToScaleSetPriority[*vm.Properties.Priority]
1005+
}
1006+
return ""
1007+
}

pkg/providers/instancetype/instancetype.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ func computeRequirements(
175175
scheduling.NewRequirement(v1beta1.LabelSKUGPUName, corev1.NodeSelectorOpDoesNotExist),
176176
scheduling.NewRequirement(v1beta1.AKSLabelCluster, corev1.NodeSelectorOpIn, labels.NormalizeClusterResourceGroupNameForLabel(opts.NodeResourceGroup)),
177177
scheduling.NewRequirement(v1beta1.AKSLabelMode, corev1.NodeSelectorOpIn, v1beta1.ModeSystem, v1beta1.ModeUser),
178+
scheduling.NewRequirement(v1beta1.AKSLabelScaleSetPriority, corev1.NodeSelectorOpIn, v1beta1.ScaleSetPriorityRegular, v1beta1.ScaleSetPrioritySpot),
178179

179180
// composites
180181
scheduling.NewRequirement(v1beta1.LabelSKUName, corev1.NodeSelectorOpDoesNotExist),

pkg/providers/instancetype/instancetypes.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ func (p *DefaultProvider) createOfferings(sku *skewer.SKU, zones sets.Set[string
228228
onDemandOffering := &cloudprovider.Offering{
229229
Requirements: scheduling.NewRequirements(
230230
scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, karpv1.CapacityTypeOnDemand),
231+
scheduling.NewRequirement(v1beta1.AKSLabelScaleSetPriority, corev1.NodeSelectorOpIn, v1beta1.ScaleSetPriorityRegular),
231232
scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpIn, zone),
232233
),
233234
Price: onDemandPrice,
@@ -237,6 +238,7 @@ func (p *DefaultProvider) createOfferings(sku *skewer.SKU, zones sets.Set[string
237238
spotOffering := &cloudprovider.Offering{
238239
Requirements: scheduling.NewRequirements(
239240
scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, karpv1.CapacityTypeSpot),
241+
scheduling.NewRequirement(v1beta1.AKSLabelScaleSetPriority, corev1.NodeSelectorOpIn, v1beta1.ScaleSetPrioritySpot),
240242
scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpIn, zone),
241243
),
242244
Price: spotPrice,

pkg/providers/instancetype/suite_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,7 @@ var _ = Describe("InstanceType Provider", func() {
10741074
Expect(*vm.Properties.StorageProfile.OSDisk.DiskSizeGB).To(Equal(int32(128)))
10751075
Expect(vm.Properties.StorageProfile.OSDisk.DiffDiskSettings).To(BeNil())
10761076
})
1077+
10771078
It("should select NvmeDisk for v6 skus with maxNvmeDiskSize > 0", func() {
10781079
nodePool.Spec.Template.Spec.Requirements = append(nodePool.Spec.Template.Spec.Requirements, karpv1.NodeSelectorRequirementWithMinValues{
10791080
NodeSelectorRequirement: v1.NodeSelectorRequirement{
@@ -2333,6 +2334,8 @@ var _ = Describe("InstanceType Provider", func() {
23332334
{Name: v1beta1.AKSLabelMemory, Label: v1beta1.AKSLabelMemory, ValueFunc: func() string { return "8192" }, ExpectedInKubeletLabels: true, ExpectedOnNode: true},
23342335
{Name: v1beta1.AKSLabelMode + "=user", Label: v1beta1.AKSLabelMode, ValueFunc: func() string { return "user" }, ExpectedInKubeletLabels: true, ExpectedOnNode: true},
23352336
{Name: v1beta1.AKSLabelMode + "=system", Label: v1beta1.AKSLabelMode, ValueFunc: func() string { return "system" }, ExpectedInKubeletLabels: true, ExpectedOnNode: true},
2337+
{Name: v1beta1.AKSLabelScaleSetPriority + "=regular", Label: v1beta1.AKSLabelScaleSetPriority, ValueFunc: func() string { return "regular" }, ExpectedInKubeletLabels: true, ExpectedOnNode: true},
2338+
{Name: v1beta1.AKSLabelScaleSetPriority + "=spot", Label: v1beta1.AKSLabelScaleSetPriority, ValueFunc: func() string { return "spot" }, ExpectedInKubeletLabels: true, ExpectedOnNode: true},
23362339
// Deprecated Labels -- note that these are not expected in kubelet labels or on the node.
23372340
// They are written by CloudProvider so don't need to be sent to kubelet, and they aren't required on the node object because Karpenter does a mapping from
23382341
// the new labels to the old labels for compatibility.

pkg/providers/labels/labels.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func Get(
102102
// Prevent race conditions with startup taints by telling Karpenter not to sync taints from the NodeClaim to the Node
103103
// See https://github.com/kubernetes-sigs/karpenter/issues/1772
104104
labels[karpv1.NodeDoNotSyncTaintsLabelKey] = "true"
105+
labels[v1beta1.AKSLabelScaleSetPriority] = v1beta1.ScaleSetPriorityRegular
105106

106107
if opts.IsAzureCNIOverlay() {
107108
// TODO: make conditional on pod subnet

pkg/providers/labels/labels_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,20 @@ func TestGetAllSingleValuedRequirementLabels(t *testing.T) {
5858
requirements: scheduling.NewRequirements(
5959
scheduling.NewRequirement(corev1.LabelInstanceTypeStable, corev1.NodeSelectorOpIn, "Standard_D2s_v3"),
6060
scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, karpv1.CapacityTypeOnDemand, karpv1.CapacityTypeSpot),
61+
scheduling.NewRequirement(v1beta1.AKSLabelScaleSetPriority, corev1.NodeSelectorOpIn, v1beta1.ScaleSetPriorityRegular, v1beta1.ScaleSetPrioritySpot),
6162
scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpIn, "westus-1"),
6263
),
6364
expectedLabels: map[string]string{
6465
corev1.LabelInstanceTypeStable: "Standard_D2s_v3",
6566
corev1.LabelTopologyZone: "westus-1",
66-
// karpv1.CapacityTypeLabelKey should be excluded because it has multiple values
67+
// karpv1.CapacityTypeLabelKey and v1beta1.AKSLabelScaleSetPriority should be excluded because they have multiple values
6768
},
6869
},
6970
{
7071
name: "No single-valued requirements",
7172
requirements: scheduling.NewRequirements(
7273
scheduling.NewRequirement(karpv1.CapacityTypeLabelKey, corev1.NodeSelectorOpIn, karpv1.CapacityTypeOnDemand, karpv1.CapacityTypeSpot),
74+
scheduling.NewRequirement(v1beta1.AKSLabelScaleSetPriority, corev1.NodeSelectorOpIn, v1beta1.ScaleSetPriorityRegular, v1beta1.ScaleSetPrioritySpot),
7375
scheduling.NewRequirement(corev1.LabelTopologyZone, corev1.NodeSelectorOpIn, "westus-1", "westus-2"),
7476
),
7577
expectedLabels: map[string]string{},

test/suites/scheduling/suite_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ var _ = Describe("Scheduling", Ordered, ContinueOnFailure, func() {
132132
v1beta1.LabelSKUStorageEphemeralOSMaxSize: "53",
133133
v1beta1.AKSLabelCluster: env.NodeResourceGroup,
134134
v1beta1.AKSLabelMode: "system",
135+
v1beta1.AKSLabelScaleSetPriority: "regular",
135136
}
136137
selectors.Insert(lo.Keys(nodeSelector)...) // Add node selector keys to selectors used in testing to ensure we test all labels
137138
requirements := lo.MapToSlice(nodeSelector, func(key string, value string) corev1.NodeSelectorRequirement {

0 commit comments

Comments
 (0)