Merge pull request #29400 from derekwaynecarr/min-reclaim-parse

Automatic merge from submit-queue

Add parsing code in kubelet for eviction-minimum-reclaim

The kubelet parses the eviction-minimum-reclaim flag and validates it for correctness.

The first two commits are from https://github.com/kubernetes/kubernetes/pull/29329 which has already achieved LGTM.
This commit is contained in:
k8s-merge-robot 2016-07-22 21:35:35 -07:00 committed by GitHub
commit 2e53a24889
4 changed files with 79 additions and 7 deletions

View File

@ -187,7 +187,7 @@ func UnsecuredKubeletConfig(s *options.KubeletServer) (*KubeletConfig, error) {
return nil, err return nil, err
} }
thresholds, err := eviction.ParseThresholdConfig(s.EvictionHard, s.EvictionSoft, s.EvictionSoftGracePeriod) thresholds, err := eviction.ParseThresholdConfig(s.EvictionHard, s.EvictionSoft, s.EvictionSoftGracePeriod, s.EvictionMinimumReclaim)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -65,7 +65,7 @@ func validSignal(signal Signal) bool {
} }
// ParseThresholdConfig parses the flags for thresholds. // ParseThresholdConfig parses the flags for thresholds.
func ParseThresholdConfig(evictionHard, evictionSoft, evictionSoftGracePeriod string) ([]Threshold, error) { func ParseThresholdConfig(evictionHard, evictionSoft, evictionSoftGracePeriod, evictionMinimumReclaim string) ([]Threshold, error) {
results := []Threshold{} results := []Threshold{}
hardThresholds, err := parseThresholdStatements(evictionHard) hardThresholds, err := parseThresholdStatements(evictionHard)
@ -82,6 +82,10 @@ func ParseThresholdConfig(evictionHard, evictionSoft, evictionSoftGracePeriod st
if err != nil { if err != nil {
return nil, err return nil, err
} }
minReclaims, err := parseMinimumReclaims(evictionMinimumReclaim)
if err != nil {
return nil, err
}
for i := range softThresholds { for i := range softThresholds {
signal := softThresholds[i].Signal signal := softThresholds[i].Signal
period, found := gracePeriods[signal] period, found := gracePeriods[signal]
@ -91,6 +95,14 @@ func ParseThresholdConfig(evictionHard, evictionSoft, evictionSoftGracePeriod st
softThresholds[i].GracePeriod = period softThresholds[i].GracePeriod = period
} }
results = append(results, softThresholds...) results = append(results, softThresholds...)
for i := range results {
for signal, minReclaim := range minReclaims {
if results[i].Signal == signal {
results[i].MinReclaim = &minReclaim
break
}
}
}
return results, nil return results, nil
} }
@ -186,6 +198,38 @@ func parseGracePeriods(expr string) (map[Signal]time.Duration, error) {
return results, nil return results, nil
} }
// parseMinimumReclaims parses the minimum reclaim statements
func parseMinimumReclaims(expr string) (map[Signal]resource.Quantity, error) {
if len(expr) == 0 {
return nil, nil
}
results := map[Signal]resource.Quantity{}
statements := strings.Split(expr, ",")
for _, statement := range statements {
parts := strings.Split(statement, "=")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid eviction minimum reclaim syntax: %v, expected <signal>=<quantity>", statement)
}
signal := Signal(parts[0])
if !validSignal(signal) {
return nil, fmt.Errorf(unsupportedEvictionSignal, signal)
}
// check against duplicate statements
if _, found := results[signal]; found {
return nil, fmt.Errorf("duplicate eviction minimum reclaim specified for %v", signal)
}
quantity, err := resource.ParseQuantity(parts[1])
if quantity.Sign() < 0 {
return nil, fmt.Errorf("negative eviction minimum reclaim specified for %v", signal)
}
if err != nil {
return nil, err
}
results[signal] = quantity
}
return results, nil
}
// diskUsage converts used bytes into a resource quantity. // diskUsage converts used bytes into a resource quantity.
func diskUsage(fsStats *statsapi.FsStats) *resource.Quantity { func diskUsage(fsStats *statsapi.FsStats) *resource.Quantity {
if fsStats == nil || fsStats.UsedBytes == nil { if fsStats == nil || fsStats.UsedBytes == nil {

View File

@ -41,6 +41,7 @@ func TestParseThresholdConfig(t *testing.T) {
evictionHard string evictionHard string
evictionSoft string evictionSoft string
evictionSoftGracePeriod string evictionSoftGracePeriod string
evictionMinReclaim string
expectErr bool expectErr bool
expectThresholds []Threshold expectThresholds []Threshold
}{ }{
@ -48,6 +49,7 @@ func TestParseThresholdConfig(t *testing.T) {
evictionHard: "", evictionHard: "",
evictionSoft: "", evictionSoft: "",
evictionSoftGracePeriod: "", evictionSoftGracePeriod: "",
evictionMinReclaim: "",
expectErr: false, expectErr: false,
expectThresholds: []Threshold{}, expectThresholds: []Threshold{},
}, },
@ -55,18 +57,21 @@ func TestParseThresholdConfig(t *testing.T) {
evictionHard: "memory.available<150Mi", evictionHard: "memory.available<150Mi",
evictionSoft: "memory.available<300Mi", evictionSoft: "memory.available<300Mi",
evictionSoftGracePeriod: "memory.available=30s", evictionSoftGracePeriod: "memory.available=30s",
evictionMinReclaim: "memory.available=0",
expectErr: false, expectErr: false,
expectThresholds: []Threshold{ expectThresholds: []Threshold{
{ {
Signal: SignalMemoryAvailable, Signal: SignalMemoryAvailable,
Operator: OpLessThan, Operator: OpLessThan,
Value: quantityMustParse("150Mi"), Value: quantityMustParse("150Mi"),
MinReclaim: quantityMustParse("0"),
}, },
{ {
Signal: SignalMemoryAvailable, Signal: SignalMemoryAvailable,
Operator: OpLessThan, Operator: OpLessThan,
Value: quantityMustParse("300Mi"), Value: quantityMustParse("300Mi"),
GracePeriod: gracePeriod, GracePeriod: gracePeriod,
MinReclaim: quantityMustParse("0"),
}, },
}, },
}, },
@ -74,6 +79,7 @@ func TestParseThresholdConfig(t *testing.T) {
evictionHard: "mem.available<150Mi", evictionHard: "mem.available<150Mi",
evictionSoft: "", evictionSoft: "",
evictionSoftGracePeriod: "", evictionSoftGracePeriod: "",
evictionMinReclaim: "",
expectErr: true, expectErr: true,
expectThresholds: []Threshold{}, expectThresholds: []Threshold{},
}, },
@ -81,6 +87,7 @@ func TestParseThresholdConfig(t *testing.T) {
evictionHard: "memory.available<150Mi,memory.available<100Mi", evictionHard: "memory.available<150Mi,memory.available<100Mi",
evictionSoft: "", evictionSoft: "",
evictionSoftGracePeriod: "", evictionSoftGracePeriod: "",
evictionMinReclaim: "",
expectErr: true, expectErr: true,
expectThresholds: []Threshold{}, expectThresholds: []Threshold{},
}, },
@ -88,6 +95,7 @@ func TestParseThresholdConfig(t *testing.T) {
evictionHard: "memory.available<150Mi,invalid.foo<150Mi", evictionHard: "memory.available<150Mi,invalid.foo<150Mi",
evictionSoft: "", evictionSoft: "",
evictionSoftGracePeriod: "", evictionSoftGracePeriod: "",
evictionMinReclaim: "",
expectErr: true, expectErr: true,
expectThresholds: []Threshold{}, expectThresholds: []Threshold{},
}, },
@ -95,6 +103,7 @@ func TestParseThresholdConfig(t *testing.T) {
evictionHard: "", evictionHard: "",
evictionSoft: "memory.available<150Mi", evictionSoft: "memory.available<150Mi",
evictionSoftGracePeriod: "", evictionSoftGracePeriod: "",
evictionMinReclaim: "",
expectErr: true, expectErr: true,
expectThresholds: []Threshold{}, expectThresholds: []Threshold{},
}, },
@ -105,9 +114,25 @@ func TestParseThresholdConfig(t *testing.T) {
expectErr: true, expectErr: true,
expectThresholds: []Threshold{}, expectThresholds: []Threshold{},
}, },
"neg-reclaim": {
evictionHard: "",
evictionSoft: "",
evictionSoftGracePeriod: "",
evictionMinReclaim: "memory.available=-300Mi",
expectErr: true,
expectThresholds: []Threshold{},
},
"duplicate-reclaim": {
evictionHard: "",
evictionSoft: "",
evictionSoftGracePeriod: "",
evictionMinReclaim: "memory.available=-300Mi,memory.available=-100Mi",
expectErr: true,
expectThresholds: []Threshold{},
},
} }
for testName, testCase := range testCases { for testName, testCase := range testCases {
thresholds, err := ParseThresholdConfig(testCase.evictionHard, testCase.evictionSoft, testCase.evictionSoftGracePeriod) thresholds, err := ParseThresholdConfig(testCase.evictionHard, testCase.evictionSoft, testCase.evictionSoftGracePeriod, testCase.evictionMinReclaim)
if testCase.expectErr != (err != nil) { if testCase.expectErr != (err != nil) {
t.Errorf("Err not as expected, test: %v, error expected: %v, actual: %v", testName, testCase.expectErr, err) t.Errorf("Err not as expected, test: %v, error expected: %v, actual: %v", testName, testCase.expectErr, err)
} }
@ -150,7 +175,8 @@ func thresholdEqual(a Threshold, b Threshold) bool {
return a.GracePeriod == b.GracePeriod && return a.GracePeriod == b.GracePeriod &&
a.Operator == b.Operator && a.Operator == b.Operator &&
a.Signal == b.Signal && a.Signal == b.Signal &&
a.Value.Cmp(*b.Value) == 0 a.Value.Cmp(*b.Value) == 0 &&
a.MinReclaim.Cmp(*b.MinReclaim) == 0
} }
// TestOrderedByQoS ensures we order BestEffort < Burstable < Guaranteed // TestOrderedByQoS ensures we order BestEffort < Burstable < Guaranteed

View File

@ -60,6 +60,8 @@ type Threshold struct {
Value *resource.Quantity Value *resource.Quantity
// GracePeriod represents the amount of time that a threshold must be met before eviction is triggered. // GracePeriod represents the amount of time that a threshold must be met before eviction is triggered.
GracePeriod time.Duration GracePeriod time.Duration
// MinReclaim represents the minimum amount of resource to reclaim if the threshold is met.
MinReclaim *resource.Quantity
} }
// Manager evaluates when an eviction threshold for node stability has been met on the node. // Manager evaluates when an eviction threshold for node stability has been met on the node.