Skip to content

Commit

Permalink
feat: add defaults configurable runtimeClassName
Browse files Browse the repository at this point in the history
allow the setting of Pod RuntimeClassName via defaults
  • Loading branch information
BobyMCbobs committed Jun 15, 2024
1 parent 09b4cd3 commit 4adbced
Show file tree
Hide file tree
Showing 6 changed files with 434 additions and 1 deletion.
17 changes: 16 additions & 1 deletion config/core/configmaps/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ metadata:
app.kubernetes.io/component: controller
app.kubernetes.io/version: devel
annotations:
knative.dev/example-checksum: "e2f637c6"
knative.dev/example-checksum: "720ddb97"
data:
# This is the Go import path for the binary that is containerized
# and substituted here.
Expand Down Expand Up @@ -108,3 +108,18 @@ data:
# `
# This may be "none" or "prefer-spread-revision-over-nodes" (default)
# default-affinity-type: "prefer-spread-revision-over-nodes"
# runtime-class-name contains the selector for which runtimeClassName
# is selected to put in a revision.
# By default, it is not set by Knative.
#
# Example:
# runtime-class-name: |
# "":
# selector:
# use-default-runc: "yes"
# kata: {}
# gvisor:
# selector:
# use-gvisor: "please"
runtime-class-name: ""
69 changes: 69 additions & 0 deletions pkg/deployment/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ package deployment
import (
"errors"
"fmt"
"strings"
"time"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/yaml"

cm "knative.dev/pkg/configmap"
"knative.dev/pkg/ptr"
)

const (
Expand Down Expand Up @@ -70,6 +75,8 @@ const (

defaultAffinityTypeKey = "default-affinity-type"
defaultAffinityTypeValue = PreferSpreadRevisionOverNodes

RuntimeClassNameKey = "runtime-class-name"
)

var (
Expand Down Expand Up @@ -116,10 +123,53 @@ func defaultConfig() *Config {
return cfg
}

func (d Config) PodRuntimeClassName(lbs map[string]string) *string {
runtimeClassName := ""
specificity := -1
for k, v := range d.RuntimeClassNames {
if !v.Matches(lbs) || v.specificity() < specificity {
continue
}
if v.specificity() > specificity || strings.Compare(k, runtimeClassName) < 0 {
runtimeClassName = k
specificity = v.specificity()
}
}
if runtimeClassName == "" {
return nil
}
return ptr.String(runtimeClassName)
}

type RuntimeClassNameLabelSelector struct {
Selector map[string]string `json:"selector,omitempty"`
}

func (s *RuntimeClassNameLabelSelector) specificity() int {
if s.Selector == nil {
return 0
}
return len(s.Selector)
}

func (s *RuntimeClassNameLabelSelector) Matches(labels map[string]string) bool {
if s.Selector == nil {
return true
}
for label, expectedValue := range s.Selector {
value, ok := labels[label]
if !ok || expectedValue != value {
return false
}
}
return true
}

// NewConfigFromMap creates a DeploymentConfig from the supplied Map.
func NewConfigFromMap(configMap map[string]string) (*Config, error) {
nc := defaultConfig()

var runtimeClassNames string
if err := cm.Parse(configMap,
// Legacy keys for backwards compatibility
cm.AsString(DeprecatedQueueSidecarImageKey, &nc.QueueSidecarImage),
Expand Down Expand Up @@ -147,6 +197,8 @@ func NewConfigFromMap(configMap map[string]string) (*Config, error) {

cm.AsStringSet(queueSidecarTokenAudiencesKey, &nc.QueueSidecarTokenAudiences),
cm.AsString(queueSidecarRooCAKey, &nc.QueueSidecarRootCA),

cm.AsString(RuntimeClassNameKey, &runtimeClassNames),
); err != nil {
return nil, err
}
Expand Down Expand Up @@ -175,6 +227,20 @@ func NewConfigFromMap(configMap map[string]string) (*Config, error) {
return nil, fmt.Errorf("unsupported %s value: %q", defaultAffinityTypeKey, affinity)
}
}
if err := yaml.Unmarshal([]byte(runtimeClassNames), &nc.RuntimeClassNames); err != nil {
return nil, fmt.Errorf("%v cannot be parsed, please check the format", RuntimeClassNameKey)
}
if len(nc.RuntimeClassNames) > 0 {
for class, rcn := range nc.RuntimeClassNames {
if len(rcn.Selector) == 0 {
continue
}
_, err := labels.ValidatedSelectorFromSet(rcn.Selector)
if err != nil {
return nil, fmt.Errorf("%v %v selector invalid: %w", RuntimeClassNameKey, class, err)
}
}
}
return nc, nil
}

Expand Down Expand Up @@ -240,4 +306,7 @@ type Config struct {
// DefaultAffinityType is a string that controls what affinity rules will be automatically
// applied to the PodSpec of all Knative services.
DefaultAffinityType AffinityType

// RuntimeClassNames specifies which runtime the Pod will use
RuntimeClassNames map[string]RuntimeClassNameLabelSelector
}
203 changes: 203 additions & 0 deletions pkg/deployment/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import (
"github.com/google/go-cmp/cmp"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"

"knative.dev/pkg/ptr"
"knative.dev/pkg/system"
"knative.dev/serving/test/conformance/api/shared"

Expand Down Expand Up @@ -334,6 +336,89 @@ func TestControllerConfiguration(t *testing.T) {
QueueSidecarTokenAudiences: sets.New("foo"),
DefaultAffinityType: defaultAffinityTypeValue,
},
}, {
name: "runtime class name defaults to nothing",
wantErr: false,
data: map[string]string{
QueueSidecarImageKey: defaultSidecarImage,
},
wantConfig: &Config{
DigestResolutionTimeout: digestResolutionTimeoutDefault,
ProgressDeadline: ProgressDeadlineDefault,
QueueSidecarCPURequest: &QueueSidecarCPURequestDefault,
QueueSidecarImage: defaultSidecarImage,
QueueSidecarTokenAudiences: sets.New(""),
RegistriesSkippingTagResolving: sets.New("kind.local", "ko.local", "dev.local"),
RuntimeClassNames: nil,
DefaultAffinityType: defaultAffinityTypeValue,
},
}, {
name: "runtime class name with wildcard",
wantErr: false,
wantConfig: &Config{
RuntimeClassNames: map[string]RuntimeClassNameLabelSelector{
"gvisor": RuntimeClassNameLabelSelector{},
},
DigestResolutionTimeout: digestResolutionTimeoutDefault,
ProgressDeadline: ProgressDeadlineDefault,
QueueSidecarCPURequest: &QueueSidecarCPURequestDefault,
QueueSidecarImage: defaultSidecarImage,
QueueSidecarTokenAudiences: sets.New(""),
RegistriesSkippingTagResolving: sets.New("kind.local", "ko.local", "dev.local"),
DefaultAffinityType: defaultAffinityTypeValue,
},
data: map[string]string{
RuntimeClassNameKey: "gvisor: {}",
QueueSidecarImageKey: defaultSidecarImage,
},
}, {
name: "runtime class name with wildcard and label selectors",
wantErr: false,
wantConfig: &Config{
RuntimeClassNames: map[string]RuntimeClassNameLabelSelector{
"gvisor": RuntimeClassNameLabelSelector{},
"kata": RuntimeClassNameLabelSelector{
Selector: map[string]string{
"some": "value-here",
},
},
},
DigestResolutionTimeout: digestResolutionTimeoutDefault,
ProgressDeadline: ProgressDeadlineDefault,
QueueSidecarCPURequest: &QueueSidecarCPURequestDefault,
QueueSidecarImage: defaultSidecarImage,
QueueSidecarTokenAudiences: sets.New(""),
RegistriesSkippingTagResolving: sets.New("kind.local", "ko.local", "dev.local"),
DefaultAffinityType: defaultAffinityTypeValue,
},
data: map[string]string{
RuntimeClassNameKey: `---
gvisor: {}
kata:
selector:
some: value-here
`,
QueueSidecarImageKey: defaultSidecarImage,
},
}, {
name: "runtime class name with bad label selectors",
wantErr: true,
data: map[string]string{
QueueSidecarImageKey: defaultSidecarImage,
RuntimeClassNameKey: `---
gvisor: {}
kata:
selector:
"-a": " a a "
`,
},
}, {
name: "runtime class name with an unparsable format",
wantErr: true,
data: map[string]string{
QueueSidecarImageKey: defaultSidecarImage,
RuntimeClassNameKey: ` ???; 231424 `,
},
}}

for _, tt := range configTests {
Expand Down Expand Up @@ -369,3 +454,121 @@ func quantity(val string) *resource.Quantity {
r := resource.MustParse(val)
return &r
}

func TestPodRuntimeClassName(t *testing.T) {
ts := []struct {
name string
serviceLabels map[string]string
runtimeClassNames map[string]RuntimeClassNameLabelSelector
want *string
}{{
name: "empty",
serviceLabels: map[string]string{},
runtimeClassNames: nil,
want: nil,
}, {
name: "wildcard set",
serviceLabels: map[string]string{},
runtimeClassNames: map[string]RuntimeClassNameLabelSelector{
"gvisor": {},
},
want: ptr.String("gvisor"),
}, {
name: "priority with multiple label selectors and one label set",
serviceLabels: map[string]string{
"needs-two": "yes",
},
runtimeClassNames: map[string]RuntimeClassNameLabelSelector{
"one": {},
"two": {
Selector: map[string]string{
"needs-two": "yes",
},
},
"three": {
Selector: map[string]string{
"needs-two": "yes",
"needs-three": "yes",
},
},
},
want: ptr.String("two"),
}, {
name: "priority with multiple label selectors and two labels set",
serviceLabels: map[string]string{
"needs-two": "yes",
"needs-three": "yes",
},
runtimeClassNames: map[string]RuntimeClassNameLabelSelector{
"one": {},
"two": {
Selector: map[string]string{
"needs-two": "yes",
},
},
"three": {
Selector: map[string]string{
"needs-two": "yes",
"needs-three": "yes",
},
},
},
want: ptr.String("three"),
}, {
name: "set via label",
serviceLabels: map[string]string{
"very-cool": "indeed",
},
runtimeClassNames: map[string]RuntimeClassNameLabelSelector{
"gvisor": {},
"kata": {
Selector: map[string]string{
"very-cool": "indeed",
},
},
},
want: ptr.String("kata"),
}, {
name: "no default only labels with set labels",
serviceLabels: map[string]string{
"very-cool": "indeed",
},
runtimeClassNames: map[string]RuntimeClassNameLabelSelector{
"": {},
"kata": {
Selector: map[string]string{
"very-cool": "indeed",
},
},
},
want: ptr.String("kata"),
}, {
name: "no default only labels with set no labels",
serviceLabels: map[string]string{},
runtimeClassNames: map[string]RuntimeClassNameLabelSelector{
"": {},
"kata": {
Selector: map[string]string{
"very-cool": "indeed",
},
},
},
want: nil,
}}

for _, tt := range ts {
tt := tt
t.Run(tt.name, func(t *testing.T) {
if tt.serviceLabels == nil {
tt.serviceLabels = map[string]string{}
}
defaults := defaultConfig()
defaults.RuntimeClassNames = tt.runtimeClassNames
got, want := defaults.PodRuntimeClassName(tt.serviceLabels), tt.want

if !equality.Semantic.DeepEqual(got, want) {
t.Errorf("PodRuntimeClassName() = %v, wanted %v", got, want)
}
})
}
}
Loading

0 comments on commit 4adbced

Please sign in to comment.