diff --git a/pkg/api/conversion.go b/pkg/api/conversion.go index 399a4257a84..033e755f4cd 100644 --- a/pkg/api/conversion.go +++ b/pkg/api/conversion.go @@ -17,12 +17,16 @@ limitations under the License. package api import ( + "fmt" + "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/util/intstr" + utillabels "k8s.io/kubernetes/pkg/util/labels" + "k8s.io/kubernetes/pkg/util/validation/field" ) func init() { @@ -69,6 +73,9 @@ func init() { Convert_Pointer_float64_To_float64, Convert_float64_To_Pointer_float64, + + Convert_map_to_unversioned_LabelSelector, + Convert_unversioned_LabelSelector_to_map, ) } @@ -195,6 +202,7 @@ func Convert_string_To_labels_Selector(in *string, out *labels.Selector, s conve *out = selector return nil } + func Convert_string_To_fields_Selector(in *string, out *fields.Selector, s conversion.Scope) error { selector, err := fields.ParseSelector(*in) if err != nil { @@ -203,6 +211,7 @@ func Convert_string_To_fields_Selector(in *string, out *fields.Selector, s conve *out = selector return nil } + func Convert_labels_Selector_To_string(in *labels.Selector, out *string, s conversion.Scope) error { if *in == nil { return nil @@ -210,6 +219,7 @@ func Convert_labels_Selector_To_string(in *labels.Selector, out *string, s conve *out = (*in).String() return nil } + func Convert_fields_Selector_To_string(in *fields.Selector, out *string, s conversion.Scope) error { if *in == nil { return nil @@ -217,7 +227,25 @@ func Convert_fields_Selector_To_string(in *fields.Selector, out *string, s conve *out = (*in).String() return nil } + func Convert_resource_Quantity_To_resource_Quantity(in *resource.Quantity, out *resource.Quantity, s conversion.Scope) error { *out = *in return nil } + +func Convert_map_to_unversioned_LabelSelector(in *map[string]string, out *unversioned.LabelSelector, s conversion.Scope) error { + out = new(unversioned.LabelSelector) + for labelKey, labelValue := range *in { + utillabels.AddLabelToSelector(out, labelKey, labelValue) + } + return nil +} + +func Convert_unversioned_LabelSelector_to_map(in *unversioned.LabelSelector, out *map[string]string, s conversion.Scope) error { + var err error + *out, err = unversioned.LabelSelectorAsMap(in) + if err != nil { + err = field.Invalid(field.NewPath("labelSelector"), *in, fmt.Sprintf("cannot convert to old selector: %v", err)) + } + return err +} diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index 3ec93d2896e..4a44937a913 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -38,8 +38,12 @@ import ( apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" + "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime/serializer/streaming" + "k8s.io/kubernetes/pkg/runtime/serializer/versioning" "k8s.io/kubernetes/pkg/util/diff" "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/watch" @@ -144,6 +148,61 @@ func roundTripSame(t *testing.T, group testapi.TestGroup, item runtime.Object, e } } +func Convert_v1beta1_ReplicaSet_to_api_ReplicationController(in *v1beta1.ReplicaSet, out *api.ReplicationController, s conversion.Scope) error { + intermediate1 := &extensions.ReplicaSet{} + if err := v1beta1.Convert_v1beta1_ReplicaSet_To_extensions_ReplicaSet(in, intermediate1, s); err != nil { + return err + } + + intermediate2 := &v1.ReplicationController{} + if err := v1.Convert_extensions_ReplicaSet_to_v1_ReplicationController(intermediate1, intermediate2, s); err != nil { + return err + } + + return v1.Convert_v1_ReplicationController_To_api_ReplicationController(intermediate2, out, s) +} + +func TestSetControllerConversion(t *testing.T) { + if err := api.Scheme.AddConversionFuncs(Convert_v1beta1_ReplicaSet_to_api_ReplicationController); err != nil { + t.Fatal(err) + } + + rs := &extensions.ReplicaSet{} + rc := &api.ReplicationController{} + + extGroup := testapi.Extensions + defaultGroup := testapi.Default + + fuzzInternalObject(t, extGroup.InternalGroupVersion(), rs, rand.Int63()) + + t.Logf("rs._internal.extensions -> rs.v1beta1.extensions") + data, err := runtime.Encode(extGroup.Codec(), rs) + if err != nil { + t.Fatalf("unexpected encoding error: %v", err) + } + + decoder := api.Codecs.UniversalDecoder(*extGroup.GroupVersion(), *defaultGroup.GroupVersion()) + if err := versioning.EnableCrossGroupDecoding(decoder, extGroup.GroupVersion().Group, defaultGroup.GroupVersion().Group); err != nil { + t.Fatalf("unexpected error while enabling cross-group decoding: %v", err) + } + + t.Logf("rs.v1beta1.extensions -> rc._internal") + if err := runtime.DecodeInto(decoder, data, rc); err != nil { + t.Fatalf("unexpected decoding error: %v", err) + } + + t.Logf("rc._internal -> rc.v1") + data, err = runtime.Encode(defaultGroup.Codec(), rc) + if err != nil { + t.Fatalf("unexpected encoding error: %v", err) + } + + t.Logf("rc.v1 -> rs._internal.extensions") + if err := runtime.DecodeInto(decoder, data, rs); err != nil { + t.Fatalf("unexpected decoding error: %v", err) + } +} + // For debugging problems func TestSpecificKind(t *testing.T) { kind := "DaemonSet" diff --git a/pkg/api/unversioned/helpers.go b/pkg/api/unversioned/helpers.go index fbbff001975..cb2f2c5cfd0 100644 --- a/pkg/api/unversioned/helpers.go +++ b/pkg/api/unversioned/helpers.go @@ -64,6 +64,35 @@ func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) { return selector, nil } +// LabelSelectorAsMap converts the LabelSelector api type into a map of strings, ie. the +// original structure of a label selector. Operators that cannot be converted into plain +// labels (Exists, DoesNotExist, NotIn, and In with more than one value) will result in +// an error. +func LabelSelectorAsMap(ps *LabelSelector) (map[string]string, error) { + if ps == nil { + return nil, nil + } + selector := map[string]string{} + for k, v := range ps.MatchLabels { + selector[k] = v + } + for _, expr := range ps.MatchExpressions { + switch expr.Operator { + case LabelSelectorOpIn: + if len(expr.Values) != 1 { + return selector, fmt.Errorf("operator %q without a single value cannot be converted into the old label selector format", expr.Operator) + } + // Should we do anything in case this will override a previous key-value pair? + selector[expr.Key] = expr.Values[0] + case LabelSelectorOpNotIn, LabelSelectorOpExists, LabelSelectorOpDoesNotExist: + return selector, fmt.Errorf("operator %q cannot be converted into the old label selector format", expr.Operator) + default: + return selector, fmt.Errorf("%q is not a valid selector operator", expr.Operator) + } + } + return selector, nil +} + // ParseToLabelSelector parses a string representing a selector into a LabelSelector object. // Note: This function should be kept in sync with the parser in pkg/labels/selector.go func ParseToLabelSelector(selector string) (*LabelSelector, error) { diff --git a/pkg/api/unversioned/helpers_test.go b/pkg/api/unversioned/helpers_test.go index 41b8c15831a..8b0bca620fa 100644 --- a/pkg/api/unversioned/helpers_test.go +++ b/pkg/api/unversioned/helpers_test.go @@ -18,6 +18,7 @@ package unversioned import ( "reflect" + "strings" "testing" "k8s.io/kubernetes/pkg/labels" @@ -81,3 +82,73 @@ func TestLabelSelectorAsSelector(t *testing.T) { } } } + +func TestLabelSelectorAsMap(t *testing.T) { + matchLabels := map[string]string{"foo": "bar"} + matchExpressions := func(operator LabelSelectorOperator, values []string) []LabelSelectorRequirement { + return []LabelSelectorRequirement{{ + Key: "baz", + Operator: operator, + Values: values, + }} + } + + tests := []struct { + in *LabelSelector + out map[string]string + errString string + }{ + {in: nil, out: nil}, + { + in: &LabelSelector{MatchLabels: matchLabels}, + out: map[string]string{"foo": "bar"}, + }, + { + in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions(LabelSelectorOpIn, []string{"norf"})}, + out: map[string]string{"foo": "bar", "baz": "norf"}, + }, + { + in: &LabelSelector{MatchExpressions: matchExpressions(LabelSelectorOpIn, []string{"norf"})}, + out: map[string]string{"baz": "norf"}, + }, + { + in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions(LabelSelectorOpIn, []string{"norf", "qux"})}, + out: map[string]string{"foo": "bar"}, + errString: "without a single value cannot be converted", + }, + { + in: &LabelSelector{MatchExpressions: matchExpressions(LabelSelectorOpNotIn, []string{"norf", "qux"})}, + out: map[string]string{}, + errString: "cannot be converted", + }, + { + in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions(LabelSelectorOpExists, []string{})}, + out: map[string]string{"foo": "bar"}, + errString: "cannot be converted", + }, + { + in: &LabelSelector{MatchExpressions: matchExpressions(LabelSelectorOpDoesNotExist, []string{})}, + out: map[string]string{}, + errString: "cannot be converted", + }, + } + + for i, tc := range tests { + out, err := LabelSelectorAsMap(tc.in) + if err == nil && len(tc.errString) > 0 { + t.Errorf("[%v]expected error but got none.", i) + continue + } + if err != nil && len(tc.errString) == 0 { + t.Errorf("[%v]did not expect error but got: %v", i, err) + continue + } + if err != nil && len(tc.errString) > 0 && !strings.Contains(err.Error(), tc.errString) { + t.Errorf("[%v]expected error with %q but got: %v", i, tc.errString, err) + continue + } + if !reflect.DeepEqual(out, tc.out) { + t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out) + } + } +} diff --git a/pkg/api/v1/conversion.go b/pkg/api/v1/conversion.go index 642b1bc5e58..f180b3348d7 100644 --- a/pkg/api/v1/conversion.go +++ b/pkg/api/v1/conversion.go @@ -19,10 +19,13 @@ package v1 import ( "encoding/json" "fmt" + "reflect" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/validation/field" ) const ( @@ -31,6 +34,9 @@ const ( // Value used to identify mirror pods from pre-v1.1 kubelet. mirrorAnnotationValue_1_0 = "mirror" + + // annotation key prefix used to identify non-convertible json paths. + NonConvertibleAnnotationPrefix = "kubernetes.io/non-convertible" ) func addConversionFuncs(scheme *runtime.Scheme) { @@ -46,6 +52,12 @@ func addConversionFuncs(scheme *runtime.Scheme) { Convert_v1_Secret_To_api_Secret, Convert_v1_ServiceSpec_To_api_ServiceSpec, Convert_v1_ResourceList_To_api_ResourceList, + Convert_v1_ReplicationController_to_extensions_ReplicaSet, + Convert_v1_ReplicationControllerSpec_to_extensions_ReplicaSetSpec, + Convert_v1_ReplicationControllerStatus_to_extensions_ReplicaSetStatus, + Convert_extensions_ReplicaSet_to_v1_ReplicationController, + Convert_extensions_ReplicaSetSpec_to_v1_ReplicationControllerSpec, + Convert_extensions_ReplicaSetStatus_to_v1_ReplicationControllerStatus, ) if err != nil { // If one of the conversion functions is malformed, detect it immediately. @@ -199,17 +211,107 @@ func addConversionFuncs(scheme *runtime.Scheme) { } } +func Convert_v1_ReplicationController_to_extensions_ReplicaSet(in *ReplicationController, out *extensions.ReplicaSet, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*ReplicationController))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1_ReplicationControllerSpec_to_extensions_ReplicaSetSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1_ReplicationControllerStatus_to_extensions_ReplicaSetStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func Convert_v1_ReplicationControllerSpec_to_extensions_ReplicaSetSpec(in *ReplicationControllerSpec, out *extensions.ReplicaSetSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*ReplicationControllerSpec))(in) + } + out.Replicas = *in.Replicas + if in.Selector != nil { + api.Convert_map_to_unversioned_LabelSelector(&in.Selector, out.Selector, s) + } + if in.Template != nil { + if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in.Template, &out.Template, s); err != nil { + return err + } + } + return nil +} + +func Convert_v1_ReplicationControllerStatus_to_extensions_ReplicaSetStatus(in *ReplicationControllerStatus, out *extensions.ReplicaSetStatus, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*ReplicationControllerStatus))(in) + } + out.Replicas = in.Replicas + out.FullyLabeledReplicas = in.FullyLabeledReplicas + out.ObservedGeneration = in.ObservedGeneration + return nil +} + +func Convert_extensions_ReplicaSet_to_v1_ReplicationController(in *extensions.ReplicaSet, out *ReplicationController, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.ReplicaSet))(in) + } + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_extensions_ReplicaSetSpec_to_v1_ReplicationControllerSpec(&in.Spec, &out.Spec, s); err != nil { + fieldErr, ok := err.(*field.Error) + if !ok { + return err + } + if out.Annotations == nil { + out.Annotations = make(map[string]string) + } + out.Annotations[NonConvertibleAnnotationPrefix+"/"+fieldErr.Field] = reflect.ValueOf(fieldErr.BadValue).String() + } + if err := Convert_extensions_ReplicaSetStatus_to_v1_ReplicationControllerStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func Convert_extensions_ReplicaSetSpec_to_v1_ReplicationControllerSpec(in *extensions.ReplicaSetSpec, out *ReplicationControllerSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.ReplicaSetSpec))(in) + } + out.Replicas = new(int32) + *out.Replicas = in.Replicas + var invalidErr error + if in.Selector != nil { + invalidErr = api.Convert_unversioned_LabelSelector_to_map(in.Selector, &out.Selector, s) + } + out.Template = new(PodTemplateSpec) + if err := Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, out.Template, s); err != nil { + return err + } + return invalidErr +} + +func Convert_extensions_ReplicaSetStatus_to_v1_ReplicationControllerStatus(in *extensions.ReplicaSetStatus, out *ReplicationControllerStatus, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*extensions.ReplicaSetStatus))(in) + } + out.Replicas = in.Replicas + out.FullyLabeledReplicas = in.FullyLabeledReplicas + out.ObservedGeneration = in.ObservedGeneration + return nil +} + func Convert_api_ReplicationControllerSpec_To_v1_ReplicationControllerSpec(in *api.ReplicationControllerSpec, out *ReplicationControllerSpec, s conversion.Scope) error { out.Replicas = &in.Replicas out.Selector = in.Selector - //if in.TemplateRef != nil { - // out.TemplateRef = new(ObjectReference) - // if err := Convert_api_ObjectReference_To_v1_ObjectReference(in.TemplateRef, out.TemplateRef, s); err != nil { - // return err - // } - //} else { - // out.TemplateRef = nil - //} if in.Template != nil { out.Template = new(PodTemplateSpec) if err := Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in.Template, out.Template, s); err != nil { @@ -224,15 +326,6 @@ func Convert_api_ReplicationControllerSpec_To_v1_ReplicationControllerSpec(in *a func Convert_v1_ReplicationControllerSpec_To_api_ReplicationControllerSpec(in *ReplicationControllerSpec, out *api.ReplicationControllerSpec, s conversion.Scope) error { out.Replicas = *in.Replicas out.Selector = in.Selector - - //if in.TemplateRef != nil { - // out.TemplateRef = new(api.ObjectReference) - // if err := Convert_v1_ObjectReference_To_api_ObjectReference(in.TemplateRef, out.TemplateRef, s); err != nil { - // return err - // } - //} else { - // out.TemplateRef = nil - //} if in.Template != nil { out.Template = new(api.PodTemplateSpec) if err := Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in.Template, out.Template, s); err != nil {