diff --git a/pkg/cmd/cli/describe/describer.go b/pkg/cmd/cli/describe/describer.go index 7c781def0d55..7e23d0362a7a 100644 --- a/pkg/cmd/cli/describe/describer.go +++ b/pkg/cmd/cli/describe/describer.go @@ -372,7 +372,7 @@ func (d *TemplateDescriber) DescribeParameters(params []templateapi.Parameter, o } } -func (d *TemplateDescriber) DescribeObjects(objects []runtime.Object, out *tabwriter.Writer) { +func (d *TemplateDescriber) DescribeObjects(objects []runtime.Object, labels map[string]string, out *tabwriter.Writer) { formatString(out, "Objects", " ") indent := " " @@ -395,6 +395,10 @@ func (d *TemplateDescriber) DescribeObjects(objects []runtime.Object, out *tabwr } formatAnnotations(out, meta, indent) } + if len(labels) > 0 { + out.Write([]byte("\n")) + formatString(out, indent+"Common Labels", formatLabels(labels)) + } } func (d *TemplateDescriber) Describe(namespace, name string) (string, error) { @@ -410,7 +414,7 @@ func (d *TemplateDescriber) Describe(namespace, name string) (string, error) { out.Flush() d.DescribeParameters(template.Parameters, out) out.Write([]byte("\n")) - d.DescribeObjects(template.Objects, out) + d.DescribeObjects(template.Objects, template.ObjectLabels, out) return nil }) } diff --git a/pkg/template/api/types.go b/pkg/template/api/types.go index 95fb719aaa5e..634fda3424c8 100644 --- a/pkg/template/api/types.go +++ b/pkg/template/api/types.go @@ -16,6 +16,10 @@ type Template struct { // Required: A list of resources to create Objects []runtime.Object + + // Optional: ObjectLabels is a set of labels that are applied to every + // object during the Template to Config transformation + ObjectLabels map[string]string } // TemplateList is a list of Template objects. diff --git a/pkg/template/api/v1beta1/conversion.go b/pkg/template/api/v1beta1/conversion.go index 2dfdd997a06c..5768d10a1adb 100644 --- a/pkg/template/api/v1beta1/conversion.go +++ b/pkg/template/api/v1beta1/conversion.go @@ -14,12 +14,21 @@ func init() { if err := s.Convert(&in.Objects, &out.Items, 0); err != nil { return err } - return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) + //FIXME: DefaultConvert should not overwrite the Labels field on the + // the base object. This is likely a bug in the DefaultConvert + // code. For now, it is called before converting the labels. + if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil { + return err + } + return s.Convert(&in.ObjectLabels, &out.Labels, 0) }, func(in *Template, out *newer.Template, s conversion.Scope) error { if err := s.Convert(&in.Items, &out.Objects, 0); err != nil { return err } + if err := s.Convert(&in.Labels, &out.ObjectLabels, 0); err != nil { + return err + } return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) }, ) diff --git a/pkg/template/api/v1beta1/types.go b/pkg/template/api/v1beta1/types.go index 783c0509203b..573be64338be 100644 --- a/pkg/template/api/v1beta1/types.go +++ b/pkg/template/api/v1beta1/types.go @@ -17,6 +17,10 @@ type Template struct { // Optional: Parameters is an array of Parameters used during the // Template to Config transformation. Parameters []Parameter `json:"parameters,omitempty"` + + // Optional: Labels is a set of labels that are applied to every + // object during the Template to Config transformation + Labels map[string]string `json:"labels,omitempty"` } // TemplateList is a list of Template objects. diff --git a/pkg/template/api/validation/validation.go b/pkg/template/api/validation/validation.go index 24ceaffcdd08..cb4b337e9690 100644 --- a/pkg/template/api/validation/validation.go +++ b/pkg/template/api/validation/validation.go @@ -48,5 +48,6 @@ func validateTemplateBody(template *api.Template) (errs errors.ValidationErrorLi paramErr := ValidateParameter(&template.Parameters[i]) errs = append(errs, paramErr.PrefixIndex(i).Prefix("parameters")...) } + errs = append(errs, validation.ValidateLabels(template.ObjectLabels, "labels")...) return } diff --git a/pkg/template/registry/rest.go b/pkg/template/registry/rest.go index f976ce626d5b..a5c67dc87cfe 100644 --- a/pkg/template/registry/rest.go +++ b/pkg/template/registry/rest.go @@ -96,10 +96,13 @@ func (s *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, err glog.V(1).Infof(utilerr.NewAggregate(err).Error()) } - if err := template.AddConfigLabels(cfg, labels.Set{"template": tpl.Name}); len(err) > 0 { - // TODO: We don't report the processing errors to users as there is no - // good way how to do it for just some items. - glog.V(1).Infof(utilerr.NewAggregate(err).Error()) + if tpl.ObjectLabels != nil { + objectLabels := labels.Set(tpl.ObjectLabels) + if err := template.AddConfigLabels(cfg, objectLabels); len(err) > 0 { + // TODO: We don't report the processing errors to users as there is no + // good way how to do it for just some items. + glog.V(1).Infof(utilerr.NewAggregate(err).Error()) + } } return cfg, nil } diff --git a/pkg/template/registry/rest_test.go b/pkg/template/registry/rest_test.go index 7ae96e814c25..f6110e4f81b4 100644 --- a/pkg/template/registry/rest_test.go +++ b/pkg/template/registry/rest_test.go @@ -4,6 +4,7 @@ import ( "testing" kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" config "github.com/openshift/origin/pkg/config/api" template "github.com/openshift/origin/pkg/template/api" @@ -32,3 +33,49 @@ func TestNewRESTDefaultsName(t *testing.T) { t.Fatalf("unexpected return object: %#v", obj) } } + +func TestNewRESTTemplateLabels(t *testing.T) { + testLabels := map[string]string{ + "label1": "value1", + "label2": "value2", + } + storage := NewREST() + obj, err := storage.Create(nil, &template.Template{ + ObjectMeta: kapi.ObjectMeta{ + Name: "test", + }, + Objects: []runtime.Object{ + &kapi.Service{ + ObjectMeta: kapi.ObjectMeta{ + Name: "test-service", + }, + Spec: kapi.ServiceSpec{ + Port: 80, + Protocol: kapi.ProtocolTCP, + SessionAffinity: kapi.AffinityTypeNone, + }, + }, + }, + ObjectLabels: testLabels, + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + config, ok := obj.(*config.Config) + if !ok { + t.Fatalf("unexpected return object: %#v", obj) + } + svc, ok := config.Items[0].(*kapi.Service) + if !ok { + t.Fatalf("Unexpected object in config: %#v", svc) + } + for k, v := range testLabels { + value, ok := svc.Labels[k] + if !ok { + t.Fatalf("Missing output label: %s", k) + } + if value != v { + t.Fatalf("Unexpected label value: %s", value) + } + } +}