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 8, 2024
1 parent 09b4cd3 commit d639d68
Show file tree
Hide file tree
Showing 6 changed files with 391 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: ""
67 changes: 67 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 @@ -116,10 +121,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 +195,8 @@ func NewConfigFromMap(configMap map[string]string) (*Config, error) {

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

cm.AsString("runtime-class-name", &runtimeClassNames),
); err != nil {
return nil, err
}
Expand Down Expand Up @@ -175,6 +225,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("runtime-class-name cannot be parsed, please check the format")
}
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("runtime-class-name %v selector invalid: %w", class, err)
}
}
}
return nc, nil
}

Expand Down Expand Up @@ -240,4 +304,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
}
162 changes: 162 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{
"runtime-class-name": "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{
"runtime-class-name": `---
gvisor: {}
kata:
selector:
some: value-here
`,
QueueSidecarImageKey: defaultSidecarImage,
},
}, {
name: "runtime class name with bad label selectors",
wantErr: true,
data: map[string]string{
QueueSidecarImageKey: defaultSidecarImage,
"runtime-class-name": `---
gvisor: {}
kata:
selector:
"-a": " a a "
`,
},
}, {
name: "runtime class name with an unparsable format",
wantErr: true,
data: map[string]string{
QueueSidecarImageKey: defaultSidecarImage,
"runtime-class-name": ` ???; 231424 `,
},
}}

for _, tt := range configTests {
Expand Down Expand Up @@ -369,3 +454,80 @@ 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": RuntimeClassNameLabelSelector{},
},
want: ptr.String("gvisor"),
}, {
name: "set via label",
serviceLabels: map[string]string{
"very-cool": "indeed",
},
runtimeClassNames: map[string]RuntimeClassNameLabelSelector{
"gvisor": RuntimeClassNameLabelSelector{},
"kata": RuntimeClassNameLabelSelector{
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{
"": RuntimeClassNameLabelSelector{},
"kata": RuntimeClassNameLabelSelector{
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{
"": RuntimeClassNameLabelSelector{},
"kata": RuntimeClassNameLabelSelector{
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)
}
})
}
}
30 changes: 30 additions & 0 deletions pkg/deployment/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pkg/reconciler/revision/resources/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ func makePodSpec(rev *v1.Revision, cfg *config.Config) (*corev1.PodSpec, error)
podSpec := BuildPodSpec(rev, append(BuildUserContainers(rev), *queueContainer), cfg)
podSpec.Volumes = append(podSpec.Volumes, extraVolumes...)

if val := cfg.Deployment.PodRuntimeClassName(rev.ObjectMeta.Labels); podSpec.RuntimeClassName == nil {
podSpec.RuntimeClassName = val
}
if cfg.Observability.EnableVarLogCollection {
podSpec.Volumes = append(podSpec.Volumes, varLogVolume)

Expand Down
Loading

0 comments on commit d639d68

Please sign in to comment.