diff --git a/pkg/config/template.go b/pkg/config/template.go index 502ddfaa134..47c9760789b 100644 --- a/pkg/config/template.go +++ b/pkg/config/template.go @@ -960,6 +960,7 @@ allowed_annotations = [ const templateStringCrioRuntimeWorkloads = `# The workloads table defines ways to customize containers with different resources # that work based on annotations, rather than the CRI. +# Note, the behavior of this table is EXPERIMENTAL and may change at any time. # Each workload, has a name, activation_annotation, annotation_prefix and set of resources it supports mutating. # The currently supported resources are "cpu" (to configure the cpu shares) and "cpuset" to configure the cpuset. # Each resource can have a default value specified, or be empty. @@ -971,21 +972,23 @@ const templateStringCrioRuntimeWorkloads = `# The workloads table defines ways t # [crio.runtime.workloads.workload-type] # activation_annotation = "io.crio/workload" # annotation_prefix = "io.crio.workload-type" -# resources = { "cpu" = "", "cpuset" = "0-1", } +# [crio.runtime.workloads.workload-type.resources] +# cpuset = 0 +# cpushares = "0-1" # Where: # The workload name is workload-type. # To specify, the pod must have the "io.crio.workload" annotation (this is a precise string match). # This workload supports setting cpuset and cpu resources. # annotation_prefix is used to customize the different resources. # To configure the cpu shares a container gets in the example above, the pod would have to have the following annotation: -# "io.crio.workload-type.cpu/{container_name} = {shares}" +# "io.crio.workload-type/$container_name = {"cpushares": "value"}" {{ range $workload_type, $workload_config := .Workloads }} [crio.runtime.workloads.{{ $workload_type }}] activation_annotation = "{{ $workload_config.ActivationAnnotation }}" annotation_prefix = "{{ $workload_config.AnnotationPrefix }}" -{{ if $workload_config.Resources }} -resources = { {{ range $resource, $default := $workload_config.Resources }}{{ printf "%q = %q, " $resource $default }}{{ end }}} -{{ end }} +[crio.runtime.workloads.{{ $workload_type }}.resources] +cpuset = "{{ $workload_config.Resources.CPUSet }}" +cpushares = {{ $workload_config.Resources.CPUShares }} {{ end }} ` diff --git a/pkg/config/workloads.go b/pkg/config/workloads.go index c52a60c6f4b..5ecfa6453bb 100644 --- a/pkg/config/workloads.go +++ b/pkg/config/workloads.go @@ -1,18 +1,17 @@ package config import ( - "strconv" + "encoding/json" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" - libresource "k8s.io/apimachinery/pkg/api/resource" "k8s.io/kubernetes/pkg/kubelet/cm/cpuset" ) -const ( - CPUShareResource = "cpushares" - CPUSetResource = "cpuset" -) +type Resources struct { + CPUShares uint64 `json:"cpushares,omitempty"` + CPUSet string `json:"cpuset,omitempty"` +} type Workloads map[string]*WorkloadConfig @@ -30,7 +29,7 @@ type WorkloadConfig struct { // If a container is configured to use this workload, and does not specify // the annotation with the resource and value, the default value will apply. // Default values do not need to be specified. - Resources map[string]string `toml:"resources"` + Resources *Resources `toml:"resources"` } func (w Workloads) Validate() error { @@ -46,16 +45,7 @@ func (w *WorkloadConfig) Validate(workloadName string) error { if w.ActivationAnnotation == "" { return errors.Errorf("annotation shouldn't be empty for workload %q", workloadName) } - for resource, defaultValue := range w.Resources { - m, ok := mutators[resource] - if !ok { - return errors.Errorf("process resource %s for workload %s: resource not supported", resource, workloadName) - } - if err := m.ValidateDefault(defaultValue); err != nil { - return errors.Wrapf(err, "process resource %s for workload %s: default value %s invalid", resource, workloadName, defaultValue) - } - } - return nil + return w.Resources.ValidateDefaults() } func (w Workloads) MutateSpecGivenAnnotations(ctrName string, specgen *generate.Generator, sboxAnnotations map[string]string) error { @@ -63,22 +53,12 @@ func (w Workloads) MutateSpecGivenAnnotations(ctrName string, specgen *generate. if workload == nil { return nil } - for resource, defaultValue := range workload.Resources { - value := valueFromAnnotation(resource, defaultValue, workload.AnnotationPrefix, ctrName, sboxAnnotations) - if value == "" { - continue - } - - m, ok := mutators[resource] - if !ok { - // CRI-O bug - panic(errors.Errorf("resource %s is not defined", resource)) - } - - if err := m.MutateSpec(specgen, value); err != nil { - return errors.Wrapf(err, "mutating spec given workload %s", workload.ActivationAnnotation) - } + resources, err := resourcesFromAnnotation(workload.AnnotationPrefix, ctrName, sboxAnnotations, workload.Resources) + if err != nil { + return err } + resources.MutateSpec(specgen) + return nil } @@ -93,57 +73,41 @@ func (w Workloads) workloadGivenActivationAnnotation(sboxAnnotations map[string] return nil } -func valueFromAnnotation(resource, defaultValue, prefix, ctrName string, annotations map[string]string) string { - annotationKey := prefix + "." + resource + "/" + ctrName +func resourcesFromAnnotation(prefix, ctrName string, annotations map[string]string, defaultResources *Resources) (*Resources, error) { + annotationKey := prefix + "/" + ctrName value, ok := annotations[annotationKey] if !ok { - return defaultValue + return defaultResources, nil } - return value -} - -var mutators = map[string]Mutator{ - CPUShareResource: new(cpuShareMutator), - CPUSetResource: new(cpusetMutator), -} -type Mutator interface { - ValidateDefault(string) error - MutateSpec(*generate.Generator, string) error -} - -type cpusetMutator struct{} + var resources *Resources + if err := json.Unmarshal([]byte(value), &resources); err != nil { + return nil, err + } -func (m *cpusetMutator) ValidateDefault(set string) error { - if set == "" { - return nil + if resources.CPUSet == "" { + resources.CPUSet = defaultResources.CPUSet + } + if resources.CPUShares == 0 { + resources.CPUShares = defaultResources.CPUShares } - _, err := cpuset.Parse(set) - return err -} -func (*cpusetMutator) MutateSpec(specgen *generate.Generator, configuredValue string) error { - specgen.SetLinuxResourcesCPUCpus(configuredValue) - return nil + return resources, nil } -type cpuShareMutator struct{} - -func (*cpuShareMutator) ValidateDefault(cpuShare string) error { - if cpuShare == "" { +func (r *Resources) ValidateDefaults() error { + if r.CPUSet == "" { return nil } - if _, err := libresource.ParseQuantity(cpuShare); err != nil { - return err - } - return nil + _, err := cpuset.Parse(r.CPUSet) + return err } -func (*cpuShareMutator) MutateSpec(specgen *generate.Generator, configuredValue string) error { - u, err := strconv.ParseUint(configuredValue, 0, 64) - if err != nil { - return err +func (r *Resources) MutateSpec(specgen *generate.Generator) { + if r.CPUSet != "" { + specgen.SetLinuxResourcesCPUCpus(r.CPUSet) + } + if r.CPUShares != 0 { + specgen.SetLinuxResourcesCPUShares(r.CPUShares) } - specgen.SetLinuxResourcesCPUShares(u) - return nil } diff --git a/test/workloads.bats b/test/workloads.bats index 5d1a35312be..49848bfd7bf 100644 --- a/test/workloads.bats +++ b/test/workloads.bats @@ -21,7 +21,9 @@ function create_workload() { [crio.runtime.workloads.management] activation_annotation = "$activation" annotation_prefix = "$prefix" -resources = { "cpushares" = "$cpushares", "cpuset" = "$cpuset" } +[crio.runtime.workloads.management.resources] +cpushares = $cpushares +cpuset = "$cpuset" EOF } @@ -71,12 +73,12 @@ function check_cpu_fields() { start_crio - jq --arg act "$activation" --arg set "$set" --arg setkey "$prefix.cpuset/$name" \ + jq --arg act "$activation" --arg set "{\"cpuset\": \"$set\"}" --arg setkey "$prefix/$name" \ ' .annotations[$act] = "true" | .annotations[$setkey] = $set' \ "$TESTDATA"/sandbox_config.json > "$sboxconfig" - jq --arg act "$activation" --arg name "$name" --arg set "$set" --arg setkey "$prefix.cpuset/$name" \ + jq --arg act "$activation" --arg name "$name" --arg set "{\"cpuset\": \"$set\"}" --arg setkey "$prefix/$name" \ ' .annotations[$act] = "true" | .annotations[$setkey] = $set | .metadata.name = $name' \ @@ -94,12 +96,12 @@ function check_cpu_fields() { start_crio - jq --arg act "$activation" --arg set "$set" --arg setkey "$prefix.cpuset/$name" \ + jq --arg act "$activation" --arg set "{\"cpuset\": \"$set\"}" --arg setkey "$prefix/$name" \ ' .annotations[$act] = "true" | .annotations[$setkey] = $set' \ "$TESTDATA"/sandbox_config.json > "$sboxconfig" - jq --arg act "$activation" --arg name "$name" --arg set "$set" --arg setkey "$prefix.cpuset/$name" \ + jq --arg act "$activation" --arg name "$name" --arg set "{\"cpuset\": \"$set\"}" --arg setkey "$prefix/$name" \ ' .annotations[$act] = "true" | .annotations[$setkey] = $set | .metadata.name = $name' \ @@ -117,11 +119,11 @@ function check_cpu_fields() { start_crio - jq --arg act "$activation" --arg set "$set" --arg setkey "$prefix.cpuset/$name" \ + jq --arg act "$activation" --arg set "{\"cpuset\": \"$set\"}" --arg setkey "$prefix/$name" \ ' .annotations[$setkey] = $set' \ "$TESTDATA"/sandbox_config.json > "$sboxconfig" - jq --arg act "$activation" --arg name "$name" --arg set "$set" --arg setkey "$prefix.cpuset/$name" \ + jq --arg act "$activation" --arg name "$name" --arg set "{\"cpuset\": \"$set\"}" --arg setkey "$prefix/$name" \ ' .annotations[$setkey] = $set | .metadata.name = $name | del(.linux.resources.cpu_shares)' \