mirror of https://github.com/k3s-io/k3s
commit
13ae9a4d46
|
@ -1294,9 +1294,9 @@ func deepCopy_extensions_JobSpec(in JobSpec, out *JobSpec, c *conversion.Cloner)
|
|||
out.Completions = nil
|
||||
}
|
||||
if in.Selector != nil {
|
||||
out.Selector = make(map[string]string)
|
||||
for key, val := range in.Selector {
|
||||
out.Selector[key] = val
|
||||
out.Selector = new(PodSelector)
|
||||
if err := deepCopy_extensions_PodSelector(*in.Selector, out.Selector, c); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Selector = nil
|
||||
|
@ -1346,6 +1346,42 @@ func deepCopy_extensions_NodeUtilization(in NodeUtilization, out *NodeUtilizatio
|
|||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_extensions_PodSelector(in PodSelector, out *PodSelector, c *conversion.Cloner) error {
|
||||
if in.MatchLabels != nil {
|
||||
out.MatchLabels = make(map[string]string)
|
||||
for key, val := range in.MatchLabels {
|
||||
out.MatchLabels[key] = val
|
||||
}
|
||||
} else {
|
||||
out.MatchLabels = nil
|
||||
}
|
||||
if in.MatchExpressions != nil {
|
||||
out.MatchExpressions = make([]PodSelectorRequirement, len(in.MatchExpressions))
|
||||
for i := range in.MatchExpressions {
|
||||
if err := deepCopy_extensions_PodSelectorRequirement(in.MatchExpressions[i], &out.MatchExpressions[i], c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.MatchExpressions = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_extensions_PodSelectorRequirement(in PodSelectorRequirement, out *PodSelectorRequirement, c *conversion.Cloner) error {
|
||||
out.Key = in.Key
|
||||
out.Operator = in.Operator
|
||||
if in.Values != nil {
|
||||
out.Values = make([]string, len(in.Values))
|
||||
for i := range in.Values {
|
||||
out.Values[i] = in.Values[i]
|
||||
}
|
||||
} else {
|
||||
out.Values = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_extensions_ReplicationControllerDummy(in ReplicationControllerDummy, out *ReplicationControllerDummy, c *conversion.Cloner) error {
|
||||
if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
|
||||
return err
|
||||
|
@ -1581,6 +1617,8 @@ func init() {
|
|||
deepCopy_extensions_JobSpec,
|
||||
deepCopy_extensions_JobStatus,
|
||||
deepCopy_extensions_NodeUtilization,
|
||||
deepCopy_extensions_PodSelector,
|
||||
deepCopy_extensions_PodSelectorRequirement,
|
||||
deepCopy_extensions_ReplicationControllerDummy,
|
||||
deepCopy_extensions_ResourceConsumption,
|
||||
deepCopy_extensions_RollingUpdateDeployment,
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sort"
|
||||
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
// PodSelectorAsSelector converts the PodSelector api type into a struct that implements
|
||||
// labels.Selector
|
||||
func PodSelectorAsSelector(ps *PodSelector) (labels.Selector, error) {
|
||||
if ps == nil {
|
||||
return labels.Nothing(), nil
|
||||
}
|
||||
if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 {
|
||||
return labels.Everything(), nil
|
||||
}
|
||||
selector := labels.LabelSelector{}
|
||||
for k, v := range ps.MatchLabels {
|
||||
req, err := labels.NewRequirement(k, labels.InOperator, sets.NewString(v))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selector = append(selector, *req)
|
||||
}
|
||||
for _, expr := range ps.MatchExpressions {
|
||||
var op labels.Operator
|
||||
switch expr.Operator {
|
||||
case PodSelectorOpIn:
|
||||
op = labels.InOperator
|
||||
case PodSelectorOpNotIn:
|
||||
op = labels.NotInOperator
|
||||
case PodSelectorOpExists:
|
||||
op = labels.ExistsOperator
|
||||
case PodSelectorOpDoesNotExist:
|
||||
op = labels.DoesNotExistOperator
|
||||
default:
|
||||
return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator)
|
||||
}
|
||||
req, err := labels.NewRequirement(expr.Key, op, sets.NewString(expr.Values...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selector = append(selector, *req)
|
||||
}
|
||||
sort.Sort(labels.ByKey(selector))
|
||||
return selector, nil
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package extensions
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
)
|
||||
|
||||
func TestPodSelectorAsSelector(t *testing.T) {
|
||||
matchLabels := map[string]string{"foo": "bar"}
|
||||
matchExpressions := []PodSelectorRequirement{{
|
||||
Key: "baz",
|
||||
Operator: PodSelectorOpIn,
|
||||
Values: []string{"qux", "norf"},
|
||||
}}
|
||||
mustParse := func(s string) labels.Selector {
|
||||
out, e := labels.Parse(s)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return out
|
||||
}
|
||||
tc := []struct {
|
||||
in *PodSelector
|
||||
out labels.Selector
|
||||
expectErr bool
|
||||
}{
|
||||
{in: nil, out: labels.Nothing()},
|
||||
{in: &PodSelector{}, out: labels.Everything()},
|
||||
{
|
||||
in: &PodSelector{MatchLabels: matchLabels},
|
||||
out: mustParse("foo in (bar)"),
|
||||
},
|
||||
{
|
||||
in: &PodSelector{MatchExpressions: matchExpressions},
|
||||
out: mustParse("baz in (norf,qux)"),
|
||||
},
|
||||
{
|
||||
in: &PodSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions},
|
||||
out: mustParse("foo in (bar),baz in (norf,qux)"),
|
||||
},
|
||||
{
|
||||
in: &PodSelector{
|
||||
MatchExpressions: []PodSelectorRequirement{{
|
||||
Key: "baz",
|
||||
Operator: PodSelectorOpExists,
|
||||
Values: []string{"qux", "norf"},
|
||||
}},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tc {
|
||||
out, err := PodSelectorAsSelector(tc.in)
|
||||
if err == nil && tc.expectErr {
|
||||
t.Errorf("[%v]expected error but got none.", i)
|
||||
}
|
||||
if err != nil && !tc.expectErr {
|
||||
t.Errorf("[%v]did not expect error but got: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(out, tc.out) {
|
||||
t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -405,7 +405,7 @@ type JobSpec struct {
|
|||
Completions *int `json:"completions,omitempty"`
|
||||
|
||||
// Selector is a label query over pods that should match the pod count.
|
||||
Selector map[string]string `json:"selector"`
|
||||
Selector *PodSelector `json:"selector,omitempty"`
|
||||
|
||||
// Template is the object that describes the pod that will be created when
|
||||
// executing a job.
|
||||
|
@ -661,7 +661,7 @@ type PodSelector struct {
|
|||
MatchExpressions []PodSelectorRequirement `json:"matchExpressions,omitempty"`
|
||||
}
|
||||
|
||||
// A pod selector requirement is a selector that contains values, a key and an operator that
|
||||
// A pod selector requirement is a selector that contains values, a key, and an operator that
|
||||
// relates the key and values.
|
||||
type PodSelectorRequirement struct {
|
||||
// key is the label key that the selector applies to.
|
||||
|
@ -669,10 +669,11 @@ type PodSelectorRequirement struct {
|
|||
// operator represents a key's relationship to a set of values.
|
||||
// Valid operators ard In, NotIn, Exists and DoesNotExist.
|
||||
Operator PodSelectorOperator `json:"operator"`
|
||||
// values is a set of string values. If the operator is In or NotIn,
|
||||
// the values set must be non-empty. This array is replaced during a
|
||||
// strategic merge patch.
|
||||
Values []string `json:"stringValues,omitempty"`
|
||||
// values is an array of string values. If the operator is In or NotIn,
|
||||
// the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
// the values array must be empty. This array is replaced during a strategic
|
||||
// merge patch.
|
||||
Values []string `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
// A pod selector operator is the set of operators that can be used in a selector requirement.
|
||||
|
|
|
@ -2785,9 +2785,9 @@ func autoconvert_extensions_JobSpec_To_v1beta1_JobSpec(in *extensions.JobSpec, o
|
|||
out.Completions = nil
|
||||
}
|
||||
if in.Selector != nil {
|
||||
out.Selector = make(map[string]string)
|
||||
for key, val := range in.Selector {
|
||||
out.Selector[key] = val
|
||||
out.Selector = new(PodSelector)
|
||||
if err := convert_extensions_PodSelector_To_v1beta1_PodSelector(in.Selector, out.Selector, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Selector = nil
|
||||
|
@ -2853,6 +2853,56 @@ func convert_extensions_NodeUtilization_To_v1beta1_NodeUtilization(in *extension
|
|||
return autoconvert_extensions_NodeUtilization_To_v1beta1_NodeUtilization(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_extensions_PodSelector_To_v1beta1_PodSelector(in *extensions.PodSelector, out *PodSelector, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*extensions.PodSelector))(in)
|
||||
}
|
||||
if in.MatchLabels != nil {
|
||||
out.MatchLabels = make(map[string]string)
|
||||
for key, val := range in.MatchLabels {
|
||||
out.MatchLabels[key] = val
|
||||
}
|
||||
} else {
|
||||
out.MatchLabels = nil
|
||||
}
|
||||
if in.MatchExpressions != nil {
|
||||
out.MatchExpressions = make([]PodSelectorRequirement, len(in.MatchExpressions))
|
||||
for i := range in.MatchExpressions {
|
||||
if err := convert_extensions_PodSelectorRequirement_To_v1beta1_PodSelectorRequirement(&in.MatchExpressions[i], &out.MatchExpressions[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.MatchExpressions = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_extensions_PodSelector_To_v1beta1_PodSelector(in *extensions.PodSelector, out *PodSelector, s conversion.Scope) error {
|
||||
return autoconvert_extensions_PodSelector_To_v1beta1_PodSelector(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_extensions_PodSelectorRequirement_To_v1beta1_PodSelectorRequirement(in *extensions.PodSelectorRequirement, out *PodSelectorRequirement, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*extensions.PodSelectorRequirement))(in)
|
||||
}
|
||||
out.Key = in.Key
|
||||
out.Operator = PodSelectorOperator(in.Operator)
|
||||
if in.Values != nil {
|
||||
out.Values = make([]string, len(in.Values))
|
||||
for i := range in.Values {
|
||||
out.Values[i] = in.Values[i]
|
||||
}
|
||||
} else {
|
||||
out.Values = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_extensions_PodSelectorRequirement_To_v1beta1_PodSelectorRequirement(in *extensions.PodSelectorRequirement, out *PodSelectorRequirement, s conversion.Scope) error {
|
||||
return autoconvert_extensions_PodSelectorRequirement_To_v1beta1_PodSelectorRequirement(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_extensions_ReplicationControllerDummy_To_v1beta1_ReplicationControllerDummy(in *extensions.ReplicationControllerDummy, out *ReplicationControllerDummy, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*extensions.ReplicationControllerDummy))(in)
|
||||
|
@ -3702,9 +3752,9 @@ func autoconvert_v1beta1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensi
|
|||
out.Completions = nil
|
||||
}
|
||||
if in.Selector != nil {
|
||||
out.Selector = make(map[string]string)
|
||||
for key, val := range in.Selector {
|
||||
out.Selector[key] = val
|
||||
out.Selector = new(extensions.PodSelector)
|
||||
if err := convert_v1beta1_PodSelector_To_extensions_PodSelector(in.Selector, out.Selector, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Selector = nil
|
||||
|
@ -3770,6 +3820,56 @@ func convert_v1beta1_NodeUtilization_To_extensions_NodeUtilization(in *NodeUtili
|
|||
return autoconvert_v1beta1_NodeUtilization_To_extensions_NodeUtilization(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_v1beta1_PodSelector_To_extensions_PodSelector(in *PodSelector, out *extensions.PodSelector, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*PodSelector))(in)
|
||||
}
|
||||
if in.MatchLabels != nil {
|
||||
out.MatchLabels = make(map[string]string)
|
||||
for key, val := range in.MatchLabels {
|
||||
out.MatchLabels[key] = val
|
||||
}
|
||||
} else {
|
||||
out.MatchLabels = nil
|
||||
}
|
||||
if in.MatchExpressions != nil {
|
||||
out.MatchExpressions = make([]extensions.PodSelectorRequirement, len(in.MatchExpressions))
|
||||
for i := range in.MatchExpressions {
|
||||
if err := convert_v1beta1_PodSelectorRequirement_To_extensions_PodSelectorRequirement(&in.MatchExpressions[i], &out.MatchExpressions[i], s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.MatchExpressions = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_v1beta1_PodSelector_To_extensions_PodSelector(in *PodSelector, out *extensions.PodSelector, s conversion.Scope) error {
|
||||
return autoconvert_v1beta1_PodSelector_To_extensions_PodSelector(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_v1beta1_PodSelectorRequirement_To_extensions_PodSelectorRequirement(in *PodSelectorRequirement, out *extensions.PodSelectorRequirement, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*PodSelectorRequirement))(in)
|
||||
}
|
||||
out.Key = in.Key
|
||||
out.Operator = extensions.PodSelectorOperator(in.Operator)
|
||||
if in.Values != nil {
|
||||
out.Values = make([]string, len(in.Values))
|
||||
for i := range in.Values {
|
||||
out.Values[i] = in.Values[i]
|
||||
}
|
||||
} else {
|
||||
out.Values = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convert_v1beta1_PodSelectorRequirement_To_extensions_PodSelectorRequirement(in *PodSelectorRequirement, out *extensions.PodSelectorRequirement, s conversion.Scope) error {
|
||||
return autoconvert_v1beta1_PodSelectorRequirement_To_extensions_PodSelectorRequirement(in, out, s)
|
||||
}
|
||||
|
||||
func autoconvert_v1beta1_ReplicationControllerDummy_To_extensions_ReplicationControllerDummy(in *ReplicationControllerDummy, out *extensions.ReplicationControllerDummy, s conversion.Scope) error {
|
||||
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
|
||||
defaulting.(func(*ReplicationControllerDummy))(in)
|
||||
|
@ -4057,6 +4157,8 @@ func init() {
|
|||
autoconvert_extensions_JobStatus_To_v1beta1_JobStatus,
|
||||
autoconvert_extensions_Job_To_v1beta1_Job,
|
||||
autoconvert_extensions_NodeUtilization_To_v1beta1_NodeUtilization,
|
||||
autoconvert_extensions_PodSelectorRequirement_To_v1beta1_PodSelectorRequirement,
|
||||
autoconvert_extensions_PodSelector_To_v1beta1_PodSelector,
|
||||
autoconvert_extensions_ReplicationControllerDummy_To_v1beta1_ReplicationControllerDummy,
|
||||
autoconvert_extensions_ResourceConsumption_To_v1beta1_ResourceConsumption,
|
||||
autoconvert_extensions_RollingUpdateDeployment_To_v1beta1_RollingUpdateDeployment,
|
||||
|
@ -4140,6 +4242,8 @@ func init() {
|
|||
autoconvert_v1beta1_JobStatus_To_extensions_JobStatus,
|
||||
autoconvert_v1beta1_Job_To_extensions_Job,
|
||||
autoconvert_v1beta1_NodeUtilization_To_extensions_NodeUtilization,
|
||||
autoconvert_v1beta1_PodSelectorRequirement_To_extensions_PodSelectorRequirement,
|
||||
autoconvert_v1beta1_PodSelector_To_extensions_PodSelector,
|
||||
autoconvert_v1beta1_ReplicationControllerDummy_To_extensions_ReplicationControllerDummy,
|
||||
autoconvert_v1beta1_ResourceConsumption_To_extensions_ResourceConsumption,
|
||||
autoconvert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment,
|
||||
|
|
|
@ -1306,9 +1306,9 @@ func deepCopy_v1beta1_JobSpec(in JobSpec, out *JobSpec, c *conversion.Cloner) er
|
|||
out.Completions = nil
|
||||
}
|
||||
if in.Selector != nil {
|
||||
out.Selector = make(map[string]string)
|
||||
for key, val := range in.Selector {
|
||||
out.Selector[key] = val
|
||||
out.Selector = new(PodSelector)
|
||||
if err := deepCopy_v1beta1_PodSelector(*in.Selector, out.Selector, c); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Selector = nil
|
||||
|
@ -1358,6 +1358,42 @@ func deepCopy_v1beta1_NodeUtilization(in NodeUtilization, out *NodeUtilization,
|
|||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_v1beta1_PodSelector(in PodSelector, out *PodSelector, c *conversion.Cloner) error {
|
||||
if in.MatchLabels != nil {
|
||||
out.MatchLabels = make(map[string]string)
|
||||
for key, val := range in.MatchLabels {
|
||||
out.MatchLabels[key] = val
|
||||
}
|
||||
} else {
|
||||
out.MatchLabels = nil
|
||||
}
|
||||
if in.MatchExpressions != nil {
|
||||
out.MatchExpressions = make([]PodSelectorRequirement, len(in.MatchExpressions))
|
||||
for i := range in.MatchExpressions {
|
||||
if err := deepCopy_v1beta1_PodSelectorRequirement(in.MatchExpressions[i], &out.MatchExpressions[i], c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.MatchExpressions = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_v1beta1_PodSelectorRequirement(in PodSelectorRequirement, out *PodSelectorRequirement, c *conversion.Cloner) error {
|
||||
out.Key = in.Key
|
||||
out.Operator = in.Operator
|
||||
if in.Values != nil {
|
||||
out.Values = make([]string, len(in.Values))
|
||||
for i := range in.Values {
|
||||
out.Values[i] = in.Values[i]
|
||||
}
|
||||
} else {
|
||||
out.Values = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepCopy_v1beta1_ReplicationControllerDummy(in ReplicationControllerDummy, out *ReplicationControllerDummy, c *conversion.Cloner) error {
|
||||
if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
|
||||
return err
|
||||
|
@ -1603,6 +1639,8 @@ func init() {
|
|||
deepCopy_v1beta1_JobSpec,
|
||||
deepCopy_v1beta1_JobStatus,
|
||||
deepCopy_v1beta1_NodeUtilization,
|
||||
deepCopy_v1beta1_PodSelector,
|
||||
deepCopy_v1beta1_PodSelectorRequirement,
|
||||
deepCopy_v1beta1_ReplicationControllerDummy,
|
||||
deepCopy_v1beta1_ResourceConsumption,
|
||||
deepCopy_v1beta1_RollingUpdateDeployment,
|
||||
|
|
|
@ -92,8 +92,10 @@ func addDefaultingFuncs() {
|
|||
labels := obj.Spec.Template.Labels
|
||||
// TODO: support templates defined elsewhere when we support them in the API
|
||||
if labels != nil {
|
||||
if len(obj.Spec.Selector) == 0 {
|
||||
obj.Spec.Selector = labels
|
||||
if obj.Spec.Selector == nil {
|
||||
obj.Spec.Selector = &PodSelector{
|
||||
MatchLabels: labels,
|
||||
}
|
||||
}
|
||||
if len(obj.Labels) == 0 {
|
||||
obj.Labels = labels
|
||||
|
|
|
@ -192,7 +192,9 @@ func TestSetDefaultDeployment(t *testing.T) {
|
|||
func TestSetDefaultJob(t *testing.T) {
|
||||
expected := &Job{
|
||||
Spec: JobSpec{
|
||||
Selector: map[string]string{"job": "selector"},
|
||||
Selector: &PodSelector{
|
||||
MatchLabels: map[string]string{"job": "selector"},
|
||||
},
|
||||
Completions: newInt(1),
|
||||
Parallelism: newInt(1),
|
||||
},
|
||||
|
@ -201,7 +203,9 @@ func TestSetDefaultJob(t *testing.T) {
|
|||
// selector set explicitly, completions and parallelism - default
|
||||
{
|
||||
Spec: JobSpec{
|
||||
Selector: map[string]string{"job": "selector"},
|
||||
Selector: &PodSelector{
|
||||
MatchLabels: map[string]string{"job": "selector"},
|
||||
},
|
||||
},
|
||||
},
|
||||
// selector from template labels, completions and parallelism - default
|
||||
|
|
|
@ -412,7 +412,7 @@ type JobSpec struct {
|
|||
|
||||
// Selector is a label query over pods that should match the pod count.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors
|
||||
Selector map[string]string `json:"selector,omitempty"`
|
||||
Selector *PodSelector `json:"selector,omitempty"`
|
||||
|
||||
// Template is the object that describes the pod that will be created when
|
||||
// executing a job.
|
||||
|
@ -657,3 +657,40 @@ type ClusterAutoscalerList struct {
|
|||
|
||||
Items []ClusterAutoscaler `json:"items"`
|
||||
}
|
||||
|
||||
// A pod selector is a label query over a set of pods. The result of matchLabels and
|
||||
// matchExpressions are ANDed. An empty pod selector matches all objects. A null
|
||||
// pod selector matches no objects.
|
||||
type PodSelector struct {
|
||||
// matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
// map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
// operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
MatchLabels map[string]string `json:"matchLabels,omitempty"`
|
||||
// matchExpressions is a list of pod selector requirements. The requirements are ANDed.
|
||||
MatchExpressions []PodSelectorRequirement `json:"matchExpressions,omitempty"`
|
||||
}
|
||||
|
||||
// A pod selector requirement is a selector that contains values, a key, and an operator that
|
||||
// relates the key and values.
|
||||
type PodSelectorRequirement struct {
|
||||
// key is the label key that the selector applies to.
|
||||
Key string `json:"key" patchStrategy:"merge" patchMergeKey:"key"`
|
||||
// operator represents a key's relationship to a set of values.
|
||||
// Valid operators ard In, NotIn, Exists and DoesNotExist.
|
||||
Operator PodSelectorOperator `json:"operator"`
|
||||
// values is an array of string values. If the operator is In or NotIn,
|
||||
// the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
// the values array must be empty. This array is replaced during a strategic
|
||||
// merge patch.
|
||||
Values []string `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
// A pod selector operator is the set of operators that can be used in a selector requirement.
|
||||
type PodSelectorOperator string
|
||||
|
||||
const (
|
||||
PodSelectorOpIn PodSelectorOperator = "In"
|
||||
PodSelectorOpNotIn PodSelectorOperator = "NotIn"
|
||||
PodSelectorOpExists PodSelectorOperator = "Exists"
|
||||
PodSelectorOpDoesNotExist PodSelectorOperator = "DoesNotExist"
|
||||
)
|
||||
|
|
|
@ -363,6 +363,27 @@ func (NodeUtilization) SwaggerDoc() map[string]string {
|
|||
return map_NodeUtilization
|
||||
}
|
||||
|
||||
var map_PodSelector = map[string]string{
|
||||
"": "A pod selector is a label query over a set of pods. The result of matchLabels and matchExpressions are ANDed. An empty pod selector matches all objects. A null pod selector matches no objects.",
|
||||
"matchLabels": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
|
||||
"matchExpressions": "matchExpressions is a list of pod selector requirements. The requirements are ANDed.",
|
||||
}
|
||||
|
||||
func (PodSelector) SwaggerDoc() map[string]string {
|
||||
return map_PodSelector
|
||||
}
|
||||
|
||||
var map_PodSelectorRequirement = map[string]string{
|
||||
"": "A pod selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
|
||||
"key": "key is the label key that the selector applies to.",
|
||||
"operator": "operator represents a key's relationship to a set of values. Valid operators ard In, NotIn, Exists and DoesNotExist.",
|
||||
"values": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
|
||||
}
|
||||
|
||||
func (PodSelectorRequirement) SwaggerDoc() map[string]string {
|
||||
return map_PodSelectorRequirement
|
||||
}
|
||||
|
||||
var map_ReplicationControllerDummy = map[string]string{
|
||||
"": "Dummy definition",
|
||||
}
|
||||
|
|
|
@ -345,16 +345,19 @@ func ValidateJobSpec(spec *extensions.JobSpec) errs.ValidationErrorList {
|
|||
if spec.Completions != nil && *spec.Completions < 0 {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("completions", spec.Completions, isNegativeErrorMsg))
|
||||
}
|
||||
|
||||
selector := labels.Set(spec.Selector).AsSelector()
|
||||
if selector.Empty() {
|
||||
if spec.Selector == nil {
|
||||
allErrs = append(allErrs, errs.NewFieldRequired("selector"))
|
||||
} else {
|
||||
allErrs = append(allErrs, ValidatePodSelector(spec.Selector).Prefix("selector")...)
|
||||
}
|
||||
|
||||
labels := labels.Set(spec.Template.Labels)
|
||||
if !selector.Matches(labels) {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("template.labels", spec.Template.Labels, "selector does not match template"))
|
||||
if selector, err := extensions.PodSelectorAsSelector(spec.Selector); err == nil {
|
||||
labels := labels.Set(spec.Template.Labels)
|
||||
if !selector.Matches(labels) {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("template.metadata.labels", spec.Template.Labels, "selector does not match template"))
|
||||
}
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template).Prefix("template")...)
|
||||
if spec.Template.Spec.RestartPolicy != api.RestartPolicyOnFailure &&
|
||||
spec.Template.Spec.RestartPolicy != api.RestartPolicyNever {
|
||||
|
@ -568,3 +571,33 @@ func ValidateClusterAutoscaler(autoscaler *extensions.ClusterAutoscaler) errs.Va
|
|||
allErrs = append(allErrs, validateClusterAutoscalerSpec(autoscaler.Spec)...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidatePodSelector(ps *extensions.PodSelector) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
if ps == nil {
|
||||
return allErrs
|
||||
}
|
||||
allErrs = append(allErrs, apivalidation.ValidateLabels(ps.MatchLabels, "matchLabels")...)
|
||||
for i, expr := range ps.MatchExpressions {
|
||||
allErrs = append(allErrs, ValidatePodSelectorRequirement(expr).Prefix(fmt.Sprintf("matchExpressions.[%v]", i))...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidatePodSelectorRequirement(sr extensions.PodSelectorRequirement) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
switch sr.Operator {
|
||||
case extensions.PodSelectorOpIn, extensions.PodSelectorOpNotIn:
|
||||
if len(sr.Values) == 0 {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("values", sr.Values, "must be non-empty when operator is In or NotIn"))
|
||||
}
|
||||
case extensions.PodSelectorOpExists, extensions.PodSelectorOpDoesNotExist:
|
||||
if len(sr.Values) > 0 {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("values", sr.Values, "must be empty when operator is Exists or DoesNotExist"))
|
||||
}
|
||||
default:
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("operator", sr.Operator, "not a valid pod selector operator"))
|
||||
}
|
||||
allErrs = append(allErrs, apivalidation.ValidateLabelName(sr.Key, "key")...)
|
||||
return allErrs
|
||||
}
|
||||
|
|
|
@ -192,7 +192,6 @@ func TestValidateDaemonSetStatusUpdate(t *testing.T) {
|
|||
t.Errorf("expected failure: %s", testName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestValidateDaemonSetUpdate(t *testing.T) {
|
||||
|
@ -725,10 +724,12 @@ func TestValidateDeployment(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestValidateJob(t *testing.T) {
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validSelector := &extensions.PodSelector{
|
||||
MatchLabels: map[string]string{"a": "b"},
|
||||
}
|
||||
validPodTemplateSpec := api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
Labels: validSelector.MatchLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyOnFailure,
|
||||
|
@ -783,11 +784,10 @@ func TestValidateJob(t *testing.T) {
|
|||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Selector: map[string]string{},
|
||||
Template: validPodTemplateSpec,
|
||||
},
|
||||
},
|
||||
"spec.template.labels:selector does not match template": {
|
||||
"spec.template.metadata.labels: invalid value 'map[y:z]', Details: selector does not match template": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "myjob",
|
||||
Namespace: api.NamespaceDefault,
|
||||
|
@ -815,7 +815,7 @@ func TestValidateJob(t *testing.T) {
|
|||
Selector: validSelector,
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
Labels: validSelector.MatchLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
|
|
|
@ -382,11 +382,9 @@ func (s *StoreToJobLister) GetPodJobs(pod *api.Pod) (jobs []extensions.Job, err
|
|||
if job.Namespace != pod.Namespace {
|
||||
continue
|
||||
}
|
||||
labelSet := labels.Set(job.Spec.Selector)
|
||||
selector = labels.Set(job.Spec.Selector).AsSelector()
|
||||
|
||||
// Job with a nil or empty selector match nothing
|
||||
if labelSet.AsSelector().Empty() || !selector.Matches(labels.Set(pod.Labels)) {
|
||||
selector, _ = extensions.PodSelectorAsSelector(job.Spec.Selector)
|
||||
if !selector.Matches(labels.Set(pod.Labels)) {
|
||||
continue
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
|
|
|
@ -313,7 +313,8 @@ func (jm *JobController) syncJob(key string) error {
|
|||
return err
|
||||
}
|
||||
jobNeedsSync := jm.expectations.SatisfiedExpectations(jobKey)
|
||||
podList, err := jm.podStore.Pods(job.Namespace).List(labels.Set(job.Spec.Selector).AsSelector())
|
||||
selector, _ := extensions.PodSelectorAsSelector(job.Spec.Selector)
|
||||
podList, err := jm.podStore.Pods(job.Namespace).List(selector)
|
||||
if err != nil {
|
||||
glog.Errorf("Error getting pods for job %q: %v", key, err)
|
||||
jm.queue.Add(key)
|
||||
|
|
|
@ -43,7 +43,9 @@ func newJob(parallelism, completions int) *extensions.Job {
|
|||
Spec: extensions.JobSpec{
|
||||
Parallelism: ¶llelism,
|
||||
Completions: &completions,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Selector: &extensions.PodSelector{
|
||||
MatchLabels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
|
@ -76,7 +78,7 @@ func newPodList(count int, status api.PodPhase, job *extensions.Job) []api.Pod {
|
|||
newPod := api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: fmt.Sprintf("pod-%v", unversioned.Now().UnixNano()),
|
||||
Labels: job.Spec.Selector,
|
||||
Labels: job.Spec.Selector.MatchLabels,
|
||||
Namespace: job.Namespace,
|
||||
},
|
||||
Status: api.PodStatus{Phase: status},
|
||||
|
@ -289,7 +291,9 @@ func TestJobPodLookup(t *testing.T) {
|
|||
job: &extensions.Job{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: extensions.JobSpec{
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Selector: &extensions.PodSelector{
|
||||
MatchLabels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
pod: &api.Pod{
|
||||
|
@ -306,7 +310,15 @@ func TestJobPodLookup(t *testing.T) {
|
|||
job: &extensions.Job{
|
||||
ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "ns"},
|
||||
Spec: extensions.JobSpec{
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Selector: &extensions.PodSelector{
|
||||
MatchExpressions: []extensions.PodSelectorRequirement{
|
||||
{
|
||||
Key: "foo",
|
||||
Operator: extensions.PodSelectorOpIn,
|
||||
Values: []string{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pod: &api.Pod{
|
||||
|
|
|
@ -885,7 +885,8 @@ func describeJob(job *extensions.Job, events *api.EventList) (string, error) {
|
|||
fmt.Fprintf(out, "Name:\t%s\n", job.Name)
|
||||
fmt.Fprintf(out, "Namespace:\t%s\n", job.Namespace)
|
||||
fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&job.Spec.Template.Spec))
|
||||
fmt.Fprintf(out, "Selector:\t%s\n", labels.FormatLabels(job.Spec.Selector))
|
||||
selector, _ := extensions.PodSelectorAsSelector(job.Spec.Selector)
|
||||
fmt.Fprintf(out, "Selector:\t%s\n", selector)
|
||||
fmt.Fprintf(out, "Parallelism:\t%d\n", *job.Spec.Parallelism)
|
||||
fmt.Fprintf(out, "Completions:\t%d\n", *job.Spec.Completions)
|
||||
fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(job.Labels))
|
||||
|
|
|
@ -736,11 +736,13 @@ func printJob(job *extensions.Job, w io.Writer, withNamespace bool, wide bool, s
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
selector, _ := extensions.PodSelectorAsSelector(job.Spec.Selector)
|
||||
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\n",
|
||||
name,
|
||||
firstContainer.Name,
|
||||
firstContainer.Image,
|
||||
labels.FormatLabels(job.Spec.Selector),
|
||||
selector.String(),
|
||||
job.Status.Succeeded)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -293,7 +293,10 @@ func TestJobStop(t *testing.T) {
|
|||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Parallelism: &zero,
|
||||
Selector: map[string]string{"k1": "v1"}},
|
||||
Selector: &extensions.PodSelector{
|
||||
MatchLabels: map[string]string{"k1": "v1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
&extensions.JobList{ // LIST
|
||||
Items: []extensions.Job{
|
||||
|
@ -304,7 +307,10 @@ func TestJobStop(t *testing.T) {
|
|||
},
|
||||
Spec: extensions.JobSpec{
|
||||
Parallelism: &zero,
|
||||
Selector: map[string]string{"k1": "v1"}},
|
||||
Selector: &extensions.PodSelector{
|
||||
MatchLabels: map[string]string{"k1": "v1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -47,6 +47,18 @@ func Everything() Selector {
|
|||
return LabelSelector{}
|
||||
}
|
||||
|
||||
type nothingSelector struct{}
|
||||
|
||||
func (n nothingSelector) Matches(_ Labels) bool { return false }
|
||||
func (n nothingSelector) Empty() bool { return false }
|
||||
func (n nothingSelector) String() string { return "<null>" }
|
||||
func (n nothingSelector) Add(_ string, _ Operator, _ []string) Selector { return n }
|
||||
|
||||
// Nothing returns a selector that matches no labels
|
||||
func Nothing() Selector {
|
||||
return nothingSelector{}
|
||||
}
|
||||
|
||||
// Operator represents a key's relationship
|
||||
// to a set of values in a Requirement.
|
||||
type Operator string
|
||||
|
|
|
@ -47,7 +47,9 @@ func validNewJob() *extensions.Job {
|
|||
Spec: extensions.JobSpec{
|
||||
Completions: &completions,
|
||||
Parallelism: ¶llelism,
|
||||
Selector: map[string]string{"a": "b"},
|
||||
Selector: &extensions.PodSelector{
|
||||
MatchLabels: map[string]string{"a": "b"},
|
||||
},
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: map[string]string{"a": "b"},
|
||||
|
@ -80,7 +82,7 @@ func TestCreate(t *testing.T) {
|
|||
&extensions.Job{
|
||||
Spec: extensions.JobSpec{
|
||||
Completions: validJob.Spec.Completions,
|
||||
Selector: map[string]string{},
|
||||
Selector: &extensions.PodSelector{},
|
||||
Template: validJob.Spec.Template,
|
||||
},
|
||||
},
|
||||
|
@ -103,7 +105,7 @@ func TestUpdate(t *testing.T) {
|
|||
// invalid updateFunc
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*extensions.Job)
|
||||
object.Spec.Selector = map[string]string{}
|
||||
object.Spec.Selector = &extensions.PodSelector{}
|
||||
return object
|
||||
},
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
|
|
|
@ -32,10 +32,12 @@ func TestJobStrategy(t *testing.T) {
|
|||
t.Errorf("Job should not allow create on update")
|
||||
}
|
||||
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validSelector := &extensions.PodSelector{
|
||||
MatchLabels: map[string]string{"a": "b"},
|
||||
}
|
||||
validPodTemplateSpec := api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
Labels: validSelector.MatchLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyOnFailure,
|
||||
|
@ -95,10 +97,12 @@ func TestJobStatusStrategy(t *testing.T) {
|
|||
if StatusStrategy.AllowCreateOnUpdate() {
|
||||
t.Errorf("Job should not allow create on update")
|
||||
}
|
||||
validSelector := map[string]string{"a": "b"}
|
||||
validSelector := &extensions.PodSelector{
|
||||
MatchLabels: map[string]string{"a": "b"},
|
||||
}
|
||||
validPodTemplateSpec := api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
Labels: validSelector.MatchLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyOnFailure,
|
||||
|
|
Loading…
Reference in New Issue