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

Refactor apis.provisioning requirements #1155

Merged
merged 9 commits into from
Feb 4, 2022
26 changes: 9 additions & 17 deletions pkg/apis/provisioning/v1alpha5/constraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type Constraints struct {
// +optional
Taints Taints `json:"taints,omitempty"`
// Requirements are layered with Labels and applied to every node.
Requirements Requirements `json:"requirements,omitempty"`
Requirements Requirements `json:"requirements,inline,omitempty"`
// KubeletConfiguration are options passed to the kubelet when provisioning nodes
//+optional
KubeletConfiguration KubeletConfiguration `json:"kubeletConfiguration,omitempty"`
Expand All @@ -51,30 +51,22 @@ func (c *Constraints) ValidatePod(pod *v1.Pod) error {
if err := c.Taints.Tolerates(pod); err != nil {
return err
}
// The constraints do not support this requirement
podRequirements := PodRequirements(pod)
for _, key := range podRequirements.Keys() {
if IgnoredLabels.Has(key) {
felix-zhe-huang marked this conversation as resolved.
Show resolved Hide resolved
continue
}
if c.Requirements.Requirement(key).Len() == 0 {
return fmt.Errorf("invalid nodeSelector %q, %v not in %v", key, podRequirements.Requirement(key).UnsortedList(), c.Requirements.Requirement(key).UnsortedList())
}
// Test if pod requirements are valid
requirements := NewPodRequirements(pod)
if errs := requirements.Validate(); errs != nil {
return fmt.Errorf("pod requirements not feasible, %v", errs)
}
// The combined requirements are not compatible
combined := c.Requirements.Add(podRequirements...)
for _, key := range podRequirements.Keys() {
if combined.Requirement(key).Len() == 0 {
return fmt.Errorf("invalid nodeSelector %q, %v not in %v", key, podRequirements.Requirement(key).UnsortedList(), c.Requirements.Requirement(key).UnsortedList())
}
// Test if pod requirements are compatible
if errs := c.Requirements.Compatible(requirements); errs != nil {
return fmt.Errorf("incompatible requirements, %w", errs)
}
return nil
}

func (c *Constraints) Tighten(pod *v1.Pod) *Constraints {
return &Constraints{
Labels: c.Labels,
Requirements: c.Requirements.Add(PodRequirements(pod)...).Consolidate().WellKnown(),
Requirements: c.Requirements.Add(NewPodRequirements(pod).Requirements...).WellKnown(),
Taints: c.Taints,
Provider: c.Provider,
KubeletConfiguration: c.KubeletConfiguration,
Expand Down
41 changes: 17 additions & 24 deletions pkg/apis/provisioning/v1alpha5/provisioner_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ import (
"fmt"
"strings"

"github.com/aws/karpenter/pkg/utils/ptr"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation"
"knative.dev/pkg/apis"

"github.com/aws/karpenter/pkg/utils/functional"
"github.com/aws/karpenter/pkg/utils/ptr"
)

var (
SupportedNodeSelectorOps = []string{string(v1.NodeSelectorOpIn), string(v1.NodeSelectorOpNotIn)}
SupportedNodeSelectorOps sets.String = sets.NewString(string(v1.NodeSelectorOpIn), string(v1.NodeSelectorOpNotIn), string(v1.NodeSelectorOpExists), string(v1.NodeSelectorOpDoesNotExist))
SupportedProvisionerOps sets.String = sets.NewString(string(v1.NodeSelectorOpIn), string(v1.NodeSelectorOpExists))
)

func (p *Provisioner) Validate(ctx context.Context) (errs *apis.FieldError) {
Expand All @@ -42,7 +42,7 @@ func (s *ProvisionerSpec) validate(ctx context.Context) (errs *apis.FieldError)
return errs.Also(
s.validateTTLSecondsUntilExpired(),
s.validateTTLSecondsAfterEmpty(),
s.Constraints.Validate(ctx),
s.Validate(ctx),
)
}

Expand Down Expand Up @@ -133,29 +133,22 @@ func (c *Constraints) validateTaints() (errs *apis.FieldError) {
return errs
}

// This function is used by the provisioner validation webhook to verify the provisioner requirements.
// When this function is called, the provisioner's requirments do not include the requirements from labels.
// Provisioner requirements only support well known labels.
felix-zhe-huang marked this conversation as resolved.
Show resolved Hide resolved
func (c *Constraints) validateRequirements() (errs *apis.FieldError) {
for i, requirement := range c.Requirements {
if err := validateRequirement(requirement); err != nil {
errs = errs.Also(apis.ErrInvalidArrayValue(err, "requirements", i))
for _, requirement := range c.Requirements.Requirements {
// Ensure requirements are well known
if !WellKnownLabels.Has(requirement.Key) {
felix-zhe-huang marked this conversation as resolved.
Show resolved Hide resolved
errs = errs.Also(apis.ErrInvalidKeyName(fmt.Sprintf("%s not in %v", requirement.Key, WellKnownLabels.UnsortedList()), "key"))
}
}
return errs
}

func validateRequirement(requirement v1.NodeSelectorRequirement) (errs *apis.FieldError) {
if !WellKnownLabels.Has(requirement.Key) {
errs = errs.Also(apis.ErrInvalidKeyName(fmt.Sprintf("%s not in %v", requirement.Key, WellKnownLabels.UnsortedList()), "key"))
}
for _, err := range validation.IsQualifiedName(requirement.Key) {
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s, %s", requirement.Key, err), "key"))
}
for i, value := range requirement.Values {
for _, err := range validation.IsValidLabelValue(value) {
errs = errs.Also(apis.ErrInvalidArrayValue(fmt.Sprintf("%s, %s", value, err), "values", i))
// Ensure requirements operator is allowed
if !SupportedProvisionerOps.Has(string(requirement.Operator)) {
errs = errs.Also(apis.ErrInvalidKeyName(fmt.Sprintf("%s not in %v", requirement.Operator, SupportedProvisionerOps.UnsortedList()), "key"))
}
}
if !functional.ContainsString(SupportedNodeSelectorOps, string(requirement.Operator)) {
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s not in %s", requirement.Operator, SupportedNodeSelectorOps), "operator"))
if err := c.Requirements.Validate(); err != nil {
errs = errs.Also(err)
}
return errs
}
Loading