Browse code

Convert Config to kapi.List{} and fix Template to use runtime.Object{}

Michal Fojtik authored on 2015/01/27 23:53:47
Showing 26 changed files
... ...
@@ -1,7 +1,6 @@
1 1
 package examples
2 2
 
3 3
 import (
4
-	"fmt"
5 4
 	"io/ioutil"
6 5
 	"os"
7 6
 	"path/filepath"
... ...
@@ -9,27 +8,19 @@ import (
9 9
 	"testing"
10 10
 
11 11
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
12
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
13
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
14 12
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
15 13
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
16 14
 	//"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
17 15
 	"github.com/golang/glog"
18 16
 
19 17
 	"github.com/openshift/origin/pkg/api/latest"
20
-	buildapi "github.com/openshift/origin/pkg/build/api"
21
-	buildv "github.com/openshift/origin/pkg/build/api/validation"
22 18
 	configapi "github.com/openshift/origin/pkg/config/api"
23 19
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
24
-	deployv "github.com/openshift/origin/pkg/deploy/api/validation"
25 20
 	imageapi "github.com/openshift/origin/pkg/image/api"
26
-	imagev "github.com/openshift/origin/pkg/image/api/validation"
27 21
 	projectapi "github.com/openshift/origin/pkg/project/api"
28
-	projectv "github.com/openshift/origin/pkg/project/api/validation"
29 22
 	routeapi "github.com/openshift/origin/pkg/route/api"
30
-	routev "github.com/openshift/origin/pkg/route/api/validation"
31 23
 	templateapi "github.com/openshift/origin/pkg/template/api"
32
-	templatev "github.com/openshift/origin/pkg/template/api/validation"
24
+	"github.com/openshift/origin/pkg/util"
33 25
 )
34 26
 
35 27
 type mockService struct{}
... ...
@@ -38,79 +29,6 @@ func (mockService) ListServices(kapi.Context) (*kapi.ServiceList, error) {
38 38
 	return &kapi.ServiceList{}, nil
39 39
 }
40 40
 
41
-func validateObject(obj runtime.Object) (errors []error) {
42
-	ctx := kapi.NewDefaultContext()
43
-
44
-	if m, err := meta.Accessor(obj); err == nil {
45
-		if len(m.Namespace()) == 0 {
46
-			m.SetNamespace(kapi.NamespaceDefault)
47
-		}
48
-	}
49
-
50
-	switch t := obj.(type) {
51
-
52
-	case *kapi.ReplicationController:
53
-		errors = validation.ValidateReplicationController(t)
54
-	case *kapi.Service:
55
-		errors = validation.ValidateService(t, mockService{}, ctx)
56
-	case *kapi.Pod:
57
-		errors = validation.ValidatePod(t)
58
-
59
-	case *imageapi.Image:
60
-		errors = imagev.ValidateImage(t)
61
-	case *imageapi.ImageRepository:
62
-		// TODO: validate image repository
63
-		// 	errors = imagev.ValidateImageRepository(t)
64
-	case *imageapi.ImageRepositoryMapping:
65
-		errors = imagev.ValidateImageRepositoryMapping(t)
66
-	case *deployapi.DeploymentConfig:
67
-		errors = deployv.ValidateDeploymentConfig(t)
68
-	case *deployapi.Deployment:
69
-		errors = deployv.ValidateDeployment(t)
70
-	case *projectapi.Project:
71
-		// this is a global resource that should not have a namespace
72
-		t.Namespace = ""
73
-		errors = projectv.ValidateProject(t)
74
-	case *routeapi.Route:
75
-		errors = routev.ValidateRoute(t)
76
-
77
-	case *buildapi.BuildConfig:
78
-		errors = buildv.ValidateBuildConfig(t)
79
-	case *buildapi.Build:
80
-		errors = buildv.ValidateBuild(t)
81
-
82
-	case *templateapi.Template:
83
-		errors = templatev.ValidateTemplate(t)
84
-		for i := range t.Items {
85
-			obj, err := latest.Codec.Decode(t.Items[i].RawJSON)
86
-			if err != nil {
87
-				errors = append(errors, err)
88
-				continue
89
-			}
90
-			errors = append(errors, validateObject(obj)...)
91
-		}
92
-	case *configapi.Config:
93
-		for i := range t.Items {
94
-			obj, err := latest.Codec.Decode(t.Items[i].RawJSON)
95
-			if err != nil {
96
-				errors = append(errors, err)
97
-				continue
98
-			}
99
-			errors = append(errors, validateObject(obj)...)
100
-		}
101
-	default:
102
-		if list, err := runtime.ExtractList(obj); err == nil {
103
-			for i := range list {
104
-				errs := validateObject(list[i])
105
-				errors = append(errors, errs...)
106
-			}
107
-			return
108
-		}
109
-		return []error{fmt.Errorf("no validation defined for %#v", obj)}
110
-	}
111
-	return errors
112
-}
113
-
114 41
 func walkJSONFiles(inDir string, fn func(name, path string, data []byte)) error {
115 42
 	err := filepath.Walk(inDir, func(path string, info os.FileInfo, err error) error {
116 43
 		if err != nil {
... ...
@@ -194,7 +112,7 @@ func TestExampleObjectSchemas(t *testing.T) {
194 194
 				t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data))
195 195
 				return
196 196
 			}
197
-			if errors := validateObject(expectedType); len(errors) > 0 {
197
+			if errors := util.ValidateObject(expectedType); len(errors) > 0 {
198 198
 				t.Errorf("%s did not validate correctly: %v", path, errors)
199 199
 			}
200 200
 		})
... ...
@@ -77,7 +77,7 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
77 77
 		c.Fuzz(&j.ObjectMeta)
78 78
 		c.Fuzz(&j.Parameters)
79 79
 		// TODO: replace with structured type definition
80
-		j.Items = []runtime.RawExtension{}
80
+		j.Items = []runtime.Object{}
81 81
 	},
82 82
 	func(j *image.Image, c fuzz.Continue) {
83 83
 		c.Fuzz(&j.ObjectMeta)
... ...
@@ -88,9 +88,9 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
88 88
 		j.DockerImageReference = c.RandString()
89 89
 	},
90 90
 	func(j *config.Config, c fuzz.Continue) {
91
-		c.Fuzz(&j.ObjectMeta)
91
+		c.Fuzz(&j.ListMeta)
92 92
 		// TODO: replace with structured type definition
93
-		j.Items = []runtime.RawExtension{}
93
+		j.Items = []runtime.Object{}
94 94
 	},
95 95
 	func(intstr *util.IntOrString, c fuzz.Continue) {
96 96
 		// util.IntOrString will panic if its kind is set wrong.
... ...
@@ -34,42 +34,56 @@ func (c *Client) Images(namespace string) ImageInterface {
34 34
 	return newImages(c, namespace)
35 35
 }
36 36
 
37
+// ImageRepositories provides a REST client for ImageRepository
37 38
 func (c *Client) ImageRepositories(namespace string) ImageRepositoryInterface {
38 39
 	return newImageRepositories(c, namespace)
39 40
 }
40 41
 
42
+// ImageRepositoryMappings provides a REST client for ImageRepositoryMapping
41 43
 func (c *Client) ImageRepositoryMappings(namespace string) ImageRepositoryMappingInterface {
42 44
 	return newImageRepositoryMappings(c, namespace)
43 45
 }
44 46
 
47
+// ImageRepositoryTags provides a REST client for ImageRepositoryTag
45 48
 func (c *Client) ImageRepositoryTags(namespace string) ImageRepositoryTagInterface {
46 49
 	return newImageRepositoryTags(c, namespace)
47 50
 }
48 51
 
52
+// Deployments provides a REST client for Deployment
49 53
 func (c *Client) Deployments(namespace string) DeploymentInterface {
50 54
 	return newDeployments(c, namespace)
51 55
 }
52 56
 
57
+// DeploymentConfigs provides a REST client for DeploymentConfig
53 58
 func (c *Client) DeploymentConfigs(namespace string) DeploymentConfigInterface {
54 59
 	return newDeploymentConfigs(c, namespace)
55 60
 }
56 61
 
62
+// Routes provides a REST client for Route
57 63
 func (c *Client) Routes(namespace string) RouteInterface {
58 64
 	return newRoutes(c, namespace)
59 65
 }
60 66
 
67
+// Users provides a REST client for User
61 68
 func (c *Client) Users() UserInterface {
62 69
 	return newUsers(c)
63 70
 }
64 71
 
72
+// UserIdentityMappings provides a REST client for UserIdentityMapping
65 73
 func (c *Client) UserIdentityMappings() UserIdentityMappingInterface {
66 74
 	return newUserIdentityMappings(c)
67 75
 }
68 76
 
77
+// Projects provides a REST client for Projects
69 78
 func (c *Client) Projects() ProjectInterface {
70 79
 	return newProjects(c)
71 80
 }
72 81
 
82
+// TemplateConfigs provides a REST client for TemplateConfig
83
+func (c *Client) TemplateConfigs(namespace string) TemplateConfigInterface {
84
+	return newTemplateConfigs(c, namespace)
85
+}
86
+
73 87
 // Client is an OpenShift client object
74 88
 type Client struct {
75 89
 	*kclient.RESTClient
76 90
new file mode 100644
... ...
@@ -0,0 +1,38 @@
0
+package client
1
+
2
+import (
3
+	configapi "github.com/openshift/origin/pkg/config/api"
4
+	templateapi "github.com/openshift/origin/pkg/template/api"
5
+)
6
+
7
+// TemplateConfigNamespacer has methods to work with Image resources in a namespace
8
+type TemplateConfigsNamespacer interface {
9
+	TemplateConfigs(namespace string) TemplateConfigInterface
10
+}
11
+
12
+// TemplateConfigInterface exposes methods on Image resources.
13
+type TemplateConfigInterface interface {
14
+	Create(t *templateapi.Template) (*configapi.Config, error)
15
+}
16
+
17
+// templateConfigs implements TemplateConfigsNamespacer interface
18
+type templateConfigs struct {
19
+	r  *Client
20
+	ns string
21
+}
22
+
23
+// newTemplateConfigs returns an TemplateConfigInterface
24
+func newTemplateConfigs(c *Client, namespace string) TemplateConfigInterface {
25
+	return &templateConfigs{
26
+		r:  c,
27
+		ns: namespace,
28
+	}
29
+}
30
+
31
+// Create process the Template and return Config/List object with substituted
32
+// parameters
33
+func (c *templateConfigs) Create(in *templateapi.Template) (*configapi.Config, error) {
34
+	config := &configapi.Config{}
35
+	err := c.r.Post().Namespace(c.ns).Resource("templateConfigs").Body(in).Do().Into(config)
36
+	return config, err
37
+}
... ...
@@ -6,6 +6,7 @@ import (
6 6
 
7 7
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
8 8
 	kubecmd "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
9
+	"github.com/golang/glog"
9 10
 	"github.com/spf13/cobra"
10 11
 	"github.com/spf13/pflag"
11 12
 
... ...
@@ -47,11 +48,13 @@ func NewCommandCLI(name string) *cobra.Command {
47 47
 	f := cmd.NewFactory(clientConfig)
48 48
 	f.BindFlags(cmds.PersistentFlags())
49 49
 	out := os.Stdout
50
-
51 50
 	// Kubernetes CRUD commands
52 51
 	cmds.AddCommand(f.NewCmdGet(out))
53 52
 	cmds.AddCommand(f.NewCmdDescribe(out))
54
-	cmds.AddCommand(f.NewCmdCreate(out))
53
+	cmds.AddCommand(
54
+		// Deprecate 'osc apply' with 'osc create' command.
55
+		applyToCreate(f.NewCmdCreate(out)),
56
+	)
55 57
 	cmds.AddCommand(f.NewCmdUpdate(out))
56 58
 	cmds.AddCommand(f.NewCmdDelete(out))
57 59
 	cmds.AddCommand(kubecmd.NewCmdNamespace(out))
... ...
@@ -61,7 +64,6 @@ func NewCommandCLI(name string) *cobra.Command {
61 61
 	cmds.AddCommand(f.NewCmdProxy(out))
62 62
 
63 63
 	// Origin commands
64
-	cmds.AddCommand(cmd.NewCmdApply(f, out))
65 64
 	cmds.AddCommand(cmd.NewCmdProcess(f, out))
66 65
 
67 66
 	// Origin build commands
... ...
@@ -72,6 +74,21 @@ func NewCommandCLI(name string) *cobra.Command {
72 72
 	return cmds
73 73
 }
74 74
 
75
+// applyToCreate injects the deprecation notice about for 'apply' command into
76
+// 'create' command.
77
+// TODO: Remove this once we get rid of 'apply' in all documentation/etc.
78
+func applyToCreate(dst *cobra.Command) *cobra.Command {
79
+	dst.Aliases = append(dst.Aliases, "apply")
80
+	oldRun := dst.Run
81
+	dst.Run = func(c *cobra.Command, args []string) {
82
+		if len(os.Args) >= 2 && os.Args[2] == "apply" {
83
+			glog.Errorf("DEPRECATED: The 'apply' command is now deprecated, use 'create' instead.")
84
+		}
85
+		oldRun(c, args)
86
+	}
87
+	return dst
88
+}
89
+
75 90
 // Copy of kubectl/cmd/DefaultClientConfig, using NewNonInteractiveDeferredLoadingClientConfig
76 91
 func defaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
77 92
 	loadingRules := clientcmd.NewClientConfigLoadingRules()
78 93
deleted file mode 100644
... ...
@@ -1,76 +0,0 @@
1
-package cmd
2
-
3
-import (
4
-	"io"
5
-
6
-	kmeta "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
7
-	kubecmd "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
8
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
9
-	"github.com/golang/glog"
10
-	"github.com/spf13/cobra"
11
-
12
-	"github.com/openshift/origin/pkg/api/latest"
13
-	"github.com/openshift/origin/pkg/config"
14
-)
15
-
16
-func NewCmdApply(f *Factory, out io.Writer) *cobra.Command {
17
-	cmd := &cobra.Command{
18
-		Use:   "apply -f filename",
19
-		Short: "Perform bulk create operation on set of resources",
20
-		Long: `Create all resources contained in JSON file specified in filename or stdin
21
-
22
-NOTE: This command will be obsoleted and it is just temporary.
23
-
24
-JSON and YAML formats are accepted.
25
-
26
-Examples:
27
-  $ kubectl apply -f config.json
28
-  <creates all resources listed in config.json>
29
-
30
-  $ cat config.json | kubectl apply -f -
31
-  <creates all resources listed in config.json>`,
32
-		Run: func(cmd *cobra.Command, args []string) {
33
-			filename := kubecmd.GetFlagString(cmd, "filename")
34
-			if len(filename) == 0 {
35
-				usageError(cmd, "Must pass a filename to update")
36
-			}
37
-
38
-			schema, err := f.Validator(cmd)
39
-			checkErr(err)
40
-			mapper, typer := f.Object(cmd)
41
-			_, namespace, _, data := kubecmd.ResourceFromFile(cmd, filename, typer, mapper, schema)
42
-
43
-			if len(namespace) == 0 {
44
-				namespace = getOriginNamespace(cmd)
45
-			} else {
46
-				err := kubecmd.CompareNamespaceFromFile(cmd, namespace)
47
-				checkErr(err)
48
-			}
49
-
50
-			result, err := config.Apply(namespace, data, func(m *kmeta.RESTMapping) (*resource.Helper, error) {
51
-				client, kclient, err := f.Clients(cmd)
52
-				if err != nil {
53
-					return nil, err
54
-				}
55
-				if latest.OriginKind(m.Kind, m.APIVersion) {
56
-					return resource.NewHelper(client, m), nil
57
-				} else {
58
-					return resource.NewHelper(kclient, m), nil
59
-				}
60
-			})
61
-			checkErr(err)
62
-
63
-			for _, itemResult := range result {
64
-				if len(itemResult.Errors) == 0 {
65
-					glog.Infof(itemResult.Message)
66
-					continue
67
-				}
68
-				for _, itemError := range itemResult.Errors {
69
-					glog.Errorf("%v", itemError)
70
-				}
71
-			}
72
-		},
73
-	}
74
-	cmd.Flags().StringP("filename", "f", "", "Filename or URL to file to use to update the resource")
75
-	return cmd
76
-}
... ...
@@ -1,19 +1,39 @@
1 1
 package cmd
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"io"
5
-	"os"
6 6
 	"strings"
7 7
 
8 8
 	kubecmd "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
9 9
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
10 10
 	"github.com/golang/glog"
11
-	"github.com/openshift/origin/pkg/cmd/client"
12 11
 	"github.com/openshift/origin/pkg/template"
13 12
 	"github.com/openshift/origin/pkg/template/api"
14 13
 	"github.com/spf13/cobra"
15 14
 )
16 15
 
16
+// injectUserVars injects user specified variables into the Template
17
+func injectUserVars(cmd *cobra.Command, t *api.Template) {
18
+	values := util.StringList{}
19
+	values.Set(kubecmd.GetFlagString(cmd, "value"))
20
+	for _, keypair := range values {
21
+		p := strings.SplitN(keypair, "=", 2)
22
+		if len(p) != 2 {
23
+			glog.Errorf("Invalid parameter assignment '%s'", keypair)
24
+			continue
25
+		}
26
+		if v := template.GetParameterByName(t, p[0]); v != nil {
27
+			v.Value = p[1]
28
+			v.Generate = ""
29
+			template.AddParameter(t, *v)
30
+		} else {
31
+			glog.Errorf("Unknown parameter name '%s'", p[0])
32
+		}
33
+	}
34
+}
35
+
36
+// NewCmdProcess returns a 'process' command
17 37
 func NewCmdProcess(f *Factory, out io.Writer) *cobra.Command {
18 38
 	cmd := &cobra.Command{
19 39
 		Use:   "process -f filename",
... ...
@@ -36,78 +56,55 @@ Examples:
36 36
 
37 37
 			schema, err := f.Validator(cmd)
38 38
 			checkErr(err)
39
+
40
+			namespace := getOriginNamespace(cmd)
39 41
 			mapper, typer := f.Object(cmd)
40
-			mappings, namespace, _, data := kubecmd.ResourceFromFile(cmd, filename, typer, mapper, schema)
41
-			if len(namespace) == 0 {
42
-				namespace = getOriginNamespace(cmd)
43
-			} else {
44
-				err := kubecmd.CompareNamespaceFromFile(cmd, namespace)
45
-				checkErr(err)
46
-			}
47 42
 
48
-			mapping, err := mapper.RESTMapping("TemplateConfig", kubecmd.GetFlagString(cmd, "api-version"))
43
+			mapping, _, _, data := kubecmd.ResourceFromFile(cmd, filename, typer, mapper, schema)
44
+			obj, err := mapping.Codec.Decode(data)
49 45
 			checkErr(err)
50
-			c, _, err := f.Clients(cmd)
46
+
47
+			templateObj, ok := obj.(*api.Template)
48
+			if !ok {
49
+				checkErr(errors.New("Unable to the convert input to the Template"))
50
+			}
51
+
52
+			client, _, err := f.Clients(cmd)
51 53
 			checkErr(err)
52 54
 
53
-			// User can override Template parameters by using --value(-v) option with
54
-			// list of key-value pairs.
55
-			// TODO: This should be done on server-side to make other clients life
56
-			//			 easier.
57 55
 			if cmd.Flag("value").Changed {
58
-				values := util.StringList{}
59
-				values.Set(kubecmd.GetFlagString(cmd, "value"))
60
-				templateObj, err := mappings.Codec.Decode(data)
61
-				checkErr(err)
62
-				t := templateObj.(*api.Template)
63
-				for _, keypair := range values {
64
-					p := strings.SplitN(keypair, "=", 2)
65
-					if len(p) != 2 {
66
-						glog.Errorf("Invalid parameter assignment '%s'", keypair)
67
-						continue
68
-					}
69
-					if v := template.GetParameterByName(t, p[0]); v != nil {
70
-						v.Value = p[1]
71
-						v.Generate = ""
72
-						template.AddParameter(t, *v)
73
-					} else {
74
-						glog.Errorf("Unknown parameter name '%s'", p[0])
75
-					}
76
-				}
77
-				data, err = mapping.Codec.Encode(t)
78
-				checkErr(err)
56
+				injectUserVars(cmd, templateObj)
79 57
 			}
80 58
 
81
-			// Print template parameters will cause template stop processing.
82
-			// Users can see what parameters can be overriden and will be set in the
83
-			// template.
84
-			if kubecmd.GetFlagBool(cmd, "parameters") {
85
-				obj, err := mapping.Codec.Decode(data)
86
-				checkErr(err)
87
-				printer, err := f.Printer(cmd, mapping, kubecmd.GetFlagBool(cmd, "no-headers"))
59
+			printer, err := kubecmd.PrinterForMapping(f.Factory, cmd, mapping)
60
+			checkErr(err)
61
+
62
+			// If 'parameters' flag is set it does not do processing but only print
63
+			// the template parameters to console for inspection.
64
+			if kubecmd.GetFlagBool(cmd, "parameters") == true {
65
+				err = printer.PrintObj(templateObj, out)
88 66
 				checkErr(err)
89
-				if t, ok := obj.(*api.Template); ok {
90
-					err = printer.PrintObj(t, out)
91
-					checkErr(err)
92
-				}
93 67
 				return
94 68
 			}
95 69
 
96
-			request := c.Post().Namespace(namespace).Resource(mapping.Resource).Body(data)
97
-			result := request.Do()
98
-			body, err := result.Raw()
70
+			result, err := client.TemplateConfigs(namespace).Create(templateObj)
99 71
 			checkErr(err)
100 72
 
101
-			printer := client.JSONPrinter{}
102
-			if err := printer.Print(body, os.Stdout); err != nil {
103
-				glog.Fatalf("unable to pretty print config JSON: %v [%s]", err, string(body))
73
+			// We need to override the default output format to JSON so we can return
74
+			// processed template. Users might still be able to change the output
75
+			// format using the 'output' flag.
76
+			if !cmd.Flag("output").Changed {
77
+				cmd.Flags().Set("output", "json")
78
+				printer, _ = kubecmd.PrinterForMapping(f.Factory, cmd, mapping)
104 79
 			}
105
-
80
+			printer.PrintObj(result, out)
106 81
 		},
107 82
 	}
83
+
84
+	kubecmd.AddPrinterFlags(cmd)
85
+
108 86
 	cmd.Flags().StringP("filename", "f", "", "Filename or URL to file to use to update the resource")
109 87
 	cmd.Flags().StringP("value", "v", "", "Specify a list of key-value pairs (eg. -v FOO=BAR,BAR=FOO) to set/override parameter values")
110 88
 	cmd.Flags().BoolP("parameters", "", false, "Do not process but only print available parameters")
111
-	cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers")
112 89
 	return cmd
113 90
 }
... ...
@@ -22,8 +22,6 @@ import (
22 22
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
23 23
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
24 24
 	"github.com/golang/glog"
25
-
26
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
27 25
 	"github.com/openshift/origin/pkg/api/latest"
28 26
 	buildapi "github.com/openshift/origin/pkg/build/api"
29 27
 	buildutil "github.com/openshift/origin/pkg/build/util"
... ...
@@ -33,7 +31,6 @@ import (
33 33
 	"github.com/openshift/origin/pkg/cmd/client/image"
34 34
 	"github.com/openshift/origin/pkg/cmd/client/project"
35 35
 	"github.com/openshift/origin/pkg/cmd/client/route"
36
-	"github.com/openshift/origin/pkg/config"
37 36
 	configapi "github.com/openshift/origin/pkg/config/api"
38 37
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
39 38
 	deployclient "github.com/openshift/origin/pkg/deploy/client"
... ...
@@ -590,36 +587,8 @@ func (c *KubeConfig) executeTemplateRequest(method string, client *osclient.Clie
590 590
 }
591 591
 
592 592
 func (c *KubeConfig) executeConfigRequest(method string, clients ClientMappings) bool {
593
-	if method != "apply" {
594
-		return false
595
-	}
596
-	if len(c.Config) == 0 {
597
-		glog.Fatal("Need to pass valid configuration file (-c config.json)")
598
-	}
599
-
600
-	clientFunc := func(m *kmeta.RESTMapping) (*resource.Helper, error) {
601
-		mapping, ok := clients[m.Resource]
602
-		if !ok {
603
-			return nil, fmt.Errorf("Unable to provide REST client for %v", m.Resource)
604
-		}
605
-		return resource.NewHelper(mapping.Client, m), nil
606
-	}
607
-
608
-	result, err := config.Apply(c.getNamespace(), c.readConfigData(), clientFunc)
609
-	if err != nil {
610
-		glog.Fatalf("Error applying the config: %v", err)
611
-	}
612
-	for _, itemResult := range result {
613
-		if len(itemResult.Errors) == 0 {
614
-			glog.Infof(itemResult.Message)
615
-			continue
616
-		}
617
-		for _, itemError := range itemResult.Errors {
618
-			glog.Errorf("%v", itemError)
619
-		}
620
-	}
621
-
622
-	return true
593
+	glog.Fatalf("DEPRECATED: The 'apply' is deprecated, use 'osc create' command instead.")
594
+	return false
623 595
 }
624 596
 
625 597
 func humanReadablePrinter() *kubecfg.HumanReadablePrinter {
... ...
@@ -1,18 +1,7 @@
1 1
 package api
2 2
 
3
-import (
4
-	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
5
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
6
-)
3
+import kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7 4
 
8
-// Config contains a set of Kubernetes resources to be applied.
9
-// TODO: Unify with Kubernetes Config
10
-//       https://github.com/GoogleCloudPlatform/kubernetes/pull/1007
11
-type Config struct {
12
-	kapi.TypeMeta   `json:",inline"`
13
-	kapi.ObjectMeta `json:"metadata,omitempty"`
14
-
15
-	// Required: Items is an array of Kubernetes resources of Service,
16
-	// Pod and/or ReplicationController kind.
17
-	Items []runtime.RawExtension `json:"items"`
18
-}
5
+// Config implements the Kubernetes api.List
6
+// DEPRECATED: The version v1beta2 should use api.List instead of Config
7
+type Config kapi.List
19 8
new file mode 100644
... ...
@@ -0,0 +1,24 @@
0
+package v1beta1
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
5
+	newer "github.com/openshift/origin/pkg/config/api"
6
+)
7
+
8
+func init() {
9
+	api.Scheme.AddConversionFuncs(
10
+		func(in *newer.Config, out *Config, s conversion.Scope) error {
11
+			out.ResourceVersion = in.ListMeta.ResourceVersion
12
+			out.SelfLink = in.ListMeta.SelfLink
13
+			s.Convert(&in.Items, &out.Items, conversion.DestFromSource)
14
+			return nil
15
+		},
16
+		func(in *Config, out *newer.Config, s conversion.Scope) error {
17
+			out.ListMeta.ResourceVersion = in.ResourceVersion
18
+			out.ListMeta.SelfLink = in.SelfLink
19
+			s.Convert(&in.Items, &out.Items, conversion.DestFromSource)
20
+			return nil
21
+		},
22
+	)
23
+}
... ...
@@ -5,9 +5,8 @@ import (
5 5
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
6 6
 )
7 7
 
8
-// Config contains a set of Kubernetes resources to be applied.
9
-// TODO: Unify with Kubernetes Config
10
-//       https://github.com/GoogleCloudPlatform/kubernetes/pull/1007
8
+// Config contains a list of Kubernetes resources to be applied.
9
+// DEPRECATED: All clients should use Kubernetes api.List instead of Config.
11 10
 type Config struct {
12 11
 	kapi.TypeMeta   `json:",inline"`
13 12
 	kapi.ObjectMeta `json:"metadata,omitempty"`
... ...
@@ -1,197 +1 @@
1 1
 package config
2
-
3
-import (
4
-	"fmt"
5
-
6
-	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
-	errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8
-	kmeta "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
9
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
11
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
12
-
13
-	"github.com/openshift/origin/pkg/config/api"
14
-	deployapi "github.com/openshift/origin/pkg/deploy/api"
15
-	"github.com/openshift/origin/pkg/util"
16
-)
17
-
18
-// ApplyResult holds the response from the REST server and potential errors
19
-type ApplyResult struct {
20
-	Errors  errs.ValidationErrorList
21
-	Message string
22
-}
23
-
24
-// reportError reports the single item validation error and properly set the
25
-// prefix and index to match the Config item JSON index
26
-func reportError(allErrs *errs.ValidationErrorList, index int, err errs.ValidationError) {
27
-	i := errs.ValidationErrorList{}
28
-	*allErrs = append(*allErrs, append(i, &err).PrefixIndex(index).Prefix("item")...)
29
-}
30
-
31
-// Apply creates and manages resources defined in the Config. The create process
32
-// won't stop on error, but it will finish the job and then return error and for
33
-// each item in the config an error and status message string.
34
-func Apply(namespace string, data []byte, clientFunc func(*kmeta.RESTMapping) (*resource.Helper, error)) ([]ApplyResult, error) {
35
-	confObj, _, err := DecodeDataToObject(data)
36
-	if err != nil {
37
-		return nil, fmt.Errorf("decoding failed, %s", err.Error())
38
-	}
39
-
40
-	conf, ok := confObj.(*api.Config)
41
-	if !ok {
42
-		return nil, fmt.Errorf("unable to convert object to Config")
43
-	}
44
-
45
-	if len(conf.Items) == 0 {
46
-		return nil, fmt.Errorf("Config items must be not empty")
47
-	}
48
-
49
-	result := []ApplyResult{}
50
-	for i, item := range conf.Items {
51
-		itemErrors := errs.ValidationErrorList{}
52
-		message := ""
53
-
54
-		itemBase, mapping, err := DecodeDataToObject(item.RawJSON)
55
-		if err != nil {
56
-			reportError(&itemErrors, i, errs.ValidationError{
57
-				errs.ValidationErrorTypeInvalid,
58
-				"decode",
59
-				err,
60
-				fmt.Sprintf("unable to decode: %v", item),
61
-			})
62
-			result = append(result, ApplyResult{itemErrors.Prefix("Config"), message})
63
-			continue
64
-		}
65
-
66
-		client, err := clientFunc(mapping)
67
-		if err != nil {
68
-			reportError(&itemErrors, i, *errs.NewFieldNotSupported("client", itemBase))
69
-			result = append(result, ApplyResult{itemErrors.Prefix("Config"), message})
70
-			continue
71
-		}
72
-
73
-		jsonResource, err := mapping.Encode(itemBase)
74
-		if err != nil {
75
-			reportError(&itemErrors, i, errs.ValidationError{
76
-				errs.ValidationErrorTypeInvalid,
77
-				"encode",
78
-				err,
79
-				fmt.Sprintf("unable to encode: %v", item),
80
-			})
81
-			result = append(result, ApplyResult{itemErrors.Prefix("Config"), message})
82
-			continue
83
-		}
84
-
85
-		if err := client.Create(namespace, true, jsonResource); err != nil {
86
-			reportError(&itemErrors, i, errs.ValidationError{
87
-				errs.ValidationErrorTypeInvalid,
88
-				"create",
89
-				err,
90
-				fmt.Sprintf("unable to create: %v", string(jsonResource)),
91
-			})
92
-		} else {
93
-			itemName, _ := mapping.MetadataAccessor.Name(itemBase)
94
-			message = fmt.Sprintf("Creation succeeded for %s with name %s", mapping.Kind, itemName)
95
-		}
96
-		result = append(result, ApplyResult{itemErrors.Prefix("Config"), message})
97
-	}
98
-	return result, nil
99
-}
100
-
101
-// addReplicationControllerNestedLabels adds new label(s) to a nested labels of a single ReplicationController object
102
-func addReplicationControllerNestedLabels(obj *kapi.ReplicationController, labels labels.Set) error {
103
-	if obj.Spec.Template.Labels == nil {
104
-		obj.Spec.Template.Labels = make(map[string]string)
105
-	}
106
-	if err := util.MergeInto(obj.Spec.Template.Labels, labels, util.ErrorOnDifferentDstKeyValue); err != nil {
107
-		return fmt.Errorf("unable to add labels to Template.ReplicationController.Spec.Template: %v", err)
108
-	}
109
-	if err := util.MergeInto(obj.Spec.Template.Labels, obj.Spec.Selector, util.ErrorOnDifferentDstKeyValue); err != nil {
110
-		return fmt.Errorf("unable to add labels to Template.ReplicationController.Spec.Template: %v", err)
111
-	}
112
-	// Selector and Spec.Template.Labels must be equal
113
-	if obj.Spec.Selector == nil {
114
-		obj.Spec.Selector = make(map[string]string)
115
-	}
116
-	if err := util.MergeInto(obj.Spec.Selector, obj.Spec.Template.Labels, util.ErrorOnDifferentDstKeyValue); err != nil {
117
-		return fmt.Errorf("unable to add labels to Template.ReplicationController.Spec.Selector: %v", err)
118
-	}
119
-	return nil
120
-}
121
-
122
-// addDeploymentNestedLabels adds new label(s) to a nested labels of a single Deployment object
123
-func addDeploymentNestedLabels(obj *deployapi.Deployment, labels labels.Set) error {
124
-	if obj.ControllerTemplate.Template.Labels == nil {
125
-		obj.ControllerTemplate.Template.Labels = make(map[string]string)
126
-	}
127
-	if err := util.MergeInto(obj.ControllerTemplate.Template.Labels, labels, util.ErrorOnDifferentDstKeyValue); err != nil {
128
-		return fmt.Errorf("unable to add labels to Template.Deployment.ControllerTemplate.Template: %v", err)
129
-	}
130
-	return nil
131
-}
132
-
133
-// addDeploymentConfigNestedLabels adds new label(s) to a nested labels of a single DeploymentConfig object
134
-func addDeploymentConfigNestedLabels(obj *deployapi.DeploymentConfig, labels labels.Set) error {
135
-	if obj.Template.ControllerTemplate.Template.Labels == nil {
136
-		obj.Template.ControllerTemplate.Template.Labels = make(map[string]string)
137
-	}
138
-	if err := util.MergeInto(obj.Template.ControllerTemplate.Template.Labels, labels, util.ErrorOnDifferentDstKeyValue); err != nil {
139
-		return fmt.Errorf("unable to add labels to Template.DeploymentConfig.Template.ControllerTemplate.Template: %v", err)
140
-	}
141
-	return nil
142
-}
143
-
144
-// AddObjectLabels adds new label(s) to a single runtime.Object
145
-func AddObjectLabels(obj runtime.Object, labels labels.Set) error {
146
-	if labels == nil {
147
-		// Nothing to add
148
-		return nil
149
-	}
150
-
151
-	accessor, err := kmeta.Accessor(obj)
152
-	if err != nil {
153
-		return err
154
-	}
155
-
156
-	metaLabels := accessor.Labels()
157
-	if metaLabels == nil {
158
-		metaLabels = make(map[string]string)
159
-	}
160
-
161
-	if err := util.MergeInto(metaLabels, labels, util.ErrorOnDifferentDstKeyValue); err != nil {
162
-		return fmt.Errorf("unable to add labels to Template.%s: %v", accessor.Kind(), err)
163
-	}
164
-	accessor.SetLabels(metaLabels)
165
-
166
-	// Handle nested Labels
167
-	switch objType := obj.(type) {
168
-	case *kapi.ReplicationController:
169
-		return addReplicationControllerNestedLabels(objType, labels)
170
-	case *deployapi.Deployment:
171
-		return addDeploymentNestedLabels(objType, labels)
172
-	case *deployapi.DeploymentConfig:
173
-		return addDeploymentConfigNestedLabels(objType, labels)
174
-	}
175
-
176
-	return nil
177
-}
178
-
179
-// AddConfigLabels adds new label(s) to all resources defined in the given Config.
180
-func AddConfigLabels(c *api.Config, labels labels.Set) errs.ValidationErrorList {
181
-	itemErrors := errs.ValidationErrorList{}
182
-	for i, in := range c.Items {
183
-		obj, mapping, err := DecodeDataToObject(in.RawJSON)
184
-		if err != nil {
185
-			reportError(&itemErrors, i, *errs.NewFieldInvalid("decode", err, fmt.Sprintf("error decoding %v", in)))
186
-		}
187
-		if err := AddObjectLabels(obj, labels); err != nil {
188
-			reportError(&itemErrors, i, *errs.NewFieldInvalid("labels", err, fmt.Sprintf("error applying labels %v to %v", labels, obj)))
189
-		}
190
-		item, err := mapping.Encode(obj)
191
-		if err != nil {
192
-			reportError(&itemErrors, i, *errs.NewFieldInvalid("encode", err, fmt.Sprintf("error encoding %v", in)))
193
-		}
194
-		c.Items[i] = runtime.RawExtension{RawJSON: item}
195
-	}
196
-	return itemErrors.Prefix("Config")
197
-}
198 2
deleted file mode 100644
... ...
@@ -1,324 +0,0 @@
1
-package config
2
-
3
-import (
4
-	"fmt"
5
-	"io/ioutil"
6
-	"net/http"
7
-	"net/http/httptest"
8
-	"net/url"
9
-	"reflect"
10
-	"testing"
11
-
12
-	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
13
-	klatest "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
14
-	kmeta "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
15
-	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
16
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
17
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
18
-
19
-	clientapi "github.com/openshift/origin/pkg/cmd/client/api"
20
-	deployapi "github.com/openshift/origin/pkg/deploy/api"
21
-)
22
-
23
-func TestApplyInvalidConfig(t *testing.T) {
24
-	clients := clientapi.ClientMappings{
25
-		"InvalidClientMapping": {"InvalidClientResource", nil, nil},
26
-	}
27
-	clientFunc := func(m *kmeta.RESTMapping) (*resource.Helper, error) {
28
-		mapping, ok := clients[m.Resource]
29
-		if !ok {
30
-			return nil, fmt.Errorf("Unable to provide REST client for %v", m.Resource)
31
-		}
32
-		return resource.NewHelper(mapping.Client, m), nil
33
-	}
34
-	invalidConfigs := []string{
35
-		`{}`,
36
-		`{ "foo": "bar" }`,
37
-		`{ "items": null }`,
38
-		`{ "items": "bar" }`,
39
-		`{ "items": [ null ] }`,
40
-		`{ "items": [ { "foo": "bar" } ] }`,
41
-		`{ "items": [ { "kind": "", "apiVersion": "v1beta1" } ] }`,
42
-		`{ "items": [ { "kind": "UnknownResource", "apiVersion": "v1beta1" } ] }`,
43
-		`{ "items": [ { "kind": "InvalidClientResource", "apiVersion": "v1beta1" } ] }`,
44
-	}
45
-	for i, invalidConfig := range invalidConfigs {
46
-		result, err := Apply(kapi.NamespaceDefault, []byte(invalidConfig), clientFunc)
47
-
48
-		if i <= 3 && err == nil {
49
-			t.Errorf("Expected error while applying invalid Config '%v', result: %v", invalidConfigs[i], result)
50
-		}
51
-
52
-		for _, itemResult := range result {
53
-			if len(itemResult.Errors) > 0 {
54
-				t.Errorf("Expected error while applying invalid Config '%v'", invalidConfigs[i])
55
-			}
56
-		}
57
-	}
58
-}
59
-
60
-func TestApplySendsData(t *testing.T) {
61
-	received := make(chan bool, 1)
62
-	fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
63
-		received <- true
64
-		validUrl := "/api/v1beta1/ns/" + kapi.NamespaceDefault + "/pods"
65
-		if r.RequestURI != validUrl {
66
-			t.Errorf("Unexpected RESTClient RequestURI. Expected: %v, got: %v.", validUrl, r.RequestURI)
67
-		}
68
-	}))
69
-
70
-	uri, _ := url.Parse(fakeServer.URL + "/api/v1beta1")
71
-	fakeClient := kclient.NewRESTClient(uri, "v1beta1", kapi.Codec, false)
72
-	clients := clientapi.ClientMappings{
73
-		"pods": {"Pod", fakeClient, kapi.Codec},
74
-	}
75
-	clientFunc := func(m *kmeta.RESTMapping) (*resource.Helper, error) {
76
-		mapping, ok := clients[m.Resource]
77
-		if !ok {
78
-			return nil, fmt.Errorf("Unable to provide REST client for %v", m.Resource)
79
-		}
80
-		return resource.NewHelper(mapping.Client, m), nil
81
-	}
82
-	config := `{ "apiVersion": "v1beta1", "kind": "Config", "metadata" : { "name": "test-config" }, "items": [ { "kind": "Pod", "apiVersion": "v1beta1", "metadata": { "name": "FakePod" } } ] }`
83
-	result, err := Apply(kapi.NamespaceDefault, []byte(config), clientFunc)
84
-
85
-	if err != nil || result == nil {
86
-		t.Errorf("Unexpected error while applying valid Config '%v', result: %v, error: %v", config, result, err)
87
-	}
88
-
89
-	for _, itemResult := range result {
90
-		if len(itemResult.Errors) > 0 {
91
-			t.Errorf("Unexpected error while applying valid Config '%v': %+v", config, itemResult.Errors)
92
-		}
93
-	}
94
-
95
-	// <-received
96
-}
97
-
98
-func ExampleApply() {
99
-	kubeClient, _ := kclient.New(&kclient.Config{Host: "127.0.0.1"})
100
-	testClientMappings := clientapi.ClientMappings{
101
-		"pods":     {"Pod", kubeClient.RESTClient, klatest.Codec},
102
-		"services": {"Service", kubeClient.RESTClient, klatest.Codec},
103
-	}
104
-	clientFunc := func(m *kmeta.RESTMapping) (*resource.Helper, error) {
105
-		mapping, ok := testClientMappings[m.Resource]
106
-		if !ok {
107
-			return nil, fmt.Errorf("Unable to provide REST client for %v", m.Resource)
108
-		}
109
-		return resource.NewHelper(mapping.Client, m), nil
110
-	}
111
-	data, _ := ioutil.ReadFile("../../examples/sample-app/docker-registry-config.json")
112
-	Apply(kapi.NamespaceDefault, data, clientFunc)
113
-}
114
-
115
-type FakeLabelsResource struct {
116
-	kapi.TypeMeta   `json:",inline"`
117
-	kapi.ObjectMeta `json:"metadata,omitempty"`
118
-}
119
-
120
-func (*FakeLabelsResource) IsAnAPIObject() {}
121
-
122
-func TestAddConfigLabels(t *testing.T) {
123
-	var nilLabels map[string]string
124
-
125
-	testCases := []struct {
126
-		obj            runtime.Object
127
-		addLabels      map[string]string
128
-		err            bool
129
-		expectedLabels map[string]string
130
-	}{
131
-		{ // [0] Test nil + nil => nil
132
-			obj:            &kapi.Pod{},
133
-			addLabels:      nilLabels,
134
-			err:            false,
135
-			expectedLabels: nilLabels,
136
-		},
137
-		{ // [1] Test nil + empty labels => empty labels
138
-			obj:            &kapi.Pod{},
139
-			addLabels:      map[string]string{},
140
-			err:            false,
141
-			expectedLabels: map[string]string{},
142
-		},
143
-		{ // [2] Test obj.Labels + nil => obj.Labels
144
-			obj: &kapi.Pod{
145
-				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
146
-			},
147
-			addLabels:      nilLabels,
148
-			err:            false,
149
-			expectedLabels: map[string]string{"foo": "bar"},
150
-		},
151
-		{ // [3] Test obj.Labels + empty labels => obj.Labels
152
-			obj: &kapi.Pod{
153
-				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
154
-			},
155
-			addLabels:      map[string]string{},
156
-			err:            false,
157
-			expectedLabels: map[string]string{"foo": "bar"},
158
-		},
159
-		{ // [4] Test nil + addLabels => addLabels
160
-			obj:            &kapi.Pod{},
161
-			addLabels:      map[string]string{"foo": "bar"},
162
-			err:            false,
163
-			expectedLabels: map[string]string{"foo": "bar"},
164
-		},
165
-		{ // [5] Test obj.labels + addLabels => expectedLabels
166
-			obj: &kapi.Service{
167
-				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"baz": ""}},
168
-			},
169
-			addLabels:      map[string]string{"foo": "bar"},
170
-			err:            false,
171
-			expectedLabels: map[string]string{"foo": "bar", "baz": ""},
172
-		},
173
-		{ // [6] Test conflicting keys with the same value
174
-			obj: &kapi.Service{
175
-				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"foo": "same value"}},
176
-			},
177
-			addLabels:      map[string]string{"foo": "same value"},
178
-			err:            false,
179
-			expectedLabels: map[string]string{"foo": "same value"},
180
-		},
181
-		{ // [7] Test conflicting keys with a different value
182
-			obj: &kapi.Service{
183
-				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"foo": "first value"}},
184
-			},
185
-			addLabels:      map[string]string{"foo": "second value"},
186
-			err:            true,
187
-			expectedLabels: map[string]string{"foo": "first value"},
188
-		},
189
-		{ // [8] Test conflicting keys with the same value in ReplicationController nested labels
190
-			obj: &kapi.ReplicationController{
191
-				ObjectMeta: kapi.ObjectMeta{
192
-					Labels: map[string]string{"foo": "same value"},
193
-				},
194
-				Spec: kapi.ReplicationControllerSpec{
195
-					Template: &kapi.PodTemplateSpec{
196
-						ObjectMeta: kapi.ObjectMeta{
197
-							Labels: map[string]string{"foo": "same value"},
198
-						},
199
-					},
200
-				},
201
-			},
202
-			addLabels:      map[string]string{"foo": "same value"},
203
-			err:            false,
204
-			expectedLabels: map[string]string{"foo": "same value"},
205
-		},
206
-		{ // [9] Test conflicting keys with a different value in ReplicationController nested labels
207
-			obj: &kapi.ReplicationController{
208
-				ObjectMeta: kapi.ObjectMeta{
209
-					Labels: map[string]string{"foo": "bar"},
210
-				},
211
-				Spec: kapi.ReplicationControllerSpec{
212
-					Template: &kapi.PodTemplateSpec{
213
-						ObjectMeta: kapi.ObjectMeta{
214
-							Labels: map[string]string{"foo": "bar"},
215
-						},
216
-					},
217
-					Selector: map[string]string{"foo": "bar"},
218
-				},
219
-			},
220
-			addLabels:      map[string]string{"baz": ""},
221
-			err:            false,
222
-			expectedLabels: map[string]string{"foo": "bar", "baz": ""},
223
-		},
224
-		{ // [10] Test conflicting keys with a different value in ReplicationController nested labels
225
-			obj: &kapi.ReplicationController{
226
-				ObjectMeta: kapi.ObjectMeta{
227
-					Labels: map[string]string{"foo": "first value"},
228
-				},
229
-				Spec: kapi.ReplicationControllerSpec{
230
-					Template: &kapi.PodTemplateSpec{
231
-						ObjectMeta: kapi.ObjectMeta{
232
-							Labels: map[string]string{"foo": "first value"},
233
-						},
234
-					},
235
-					Selector: map[string]string{"foo": "first value"},
236
-				},
237
-			},
238
-			addLabels:      map[string]string{"foo": "second value"},
239
-			err:            true,
240
-			expectedLabels: map[string]string{"foo": "first value"},
241
-		},
242
-		{ // [11] Test adding labels to a Deployment object
243
-			obj: &deployapi.Deployment{
244
-				ObjectMeta: kapi.ObjectMeta{
245
-					Labels: map[string]string{"foo": "first value"},
246
-				},
247
-				ControllerTemplate: kapi.ReplicationControllerSpec{
248
-					Template: &kapi.PodTemplateSpec{
249
-						ObjectMeta: kapi.ObjectMeta{
250
-							Labels: map[string]string{"foo": "first value"},
251
-						},
252
-					},
253
-				},
254
-			},
255
-			addLabels:      map[string]string{"bar": "second value"},
256
-			err:            false,
257
-			expectedLabels: map[string]string{"foo": "first value", "bar": "second value"},
258
-		},
259
-		{ // [12] Test adding labels to a DeploymentConfig object
260
-			obj: &deployapi.DeploymentConfig{
261
-				ObjectMeta: kapi.ObjectMeta{
262
-					Labels: map[string]string{"foo": "first value"},
263
-				},
264
-				Template: deployapi.DeploymentTemplate{
265
-					ControllerTemplate: kapi.ReplicationControllerSpec{
266
-						Template: &kapi.PodTemplateSpec{
267
-							ObjectMeta: kapi.ObjectMeta{
268
-								Labels: map[string]string{"foo": "first value"},
269
-							},
270
-						},
271
-					},
272
-				},
273
-			},
274
-			addLabels:      map[string]string{"bar": "second value"},
275
-			err:            false,
276
-			expectedLabels: map[string]string{"foo": "first value", "bar": "second value"},
277
-		},
278
-		{ // [13] Test unknown Generic Object with Labels field
279
-			obj: &FakeLabelsResource{
280
-				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"baz": ""}},
281
-			},
282
-			addLabels:      map[string]string{"foo": "bar"},
283
-			err:            false,
284
-			expectedLabels: map[string]string{"foo": "bar", "baz": ""},
285
-		},
286
-	}
287
-
288
-	for i, test := range testCases {
289
-		err := AddObjectLabels(test.obj, test.addLabels)
290
-		if err != nil && !test.err {
291
-			t.Errorf("Unexpected error while setting labels on testCase[%v]: %v.", i, err)
292
-		} else if err == nil && test.err {
293
-			t.Errorf("Unexpected non-error while setting labels on testCase[%v].", i)
294
-		}
295
-
296
-		accessor, err := kmeta.Accessor(test.obj)
297
-		if err != nil {
298
-			t.Error(err)
299
-		}
300
-		metaLabels := accessor.Labels()
301
-		if e, a := test.expectedLabels, metaLabels; !reflect.DeepEqual(e, a) {
302
-			t.Errorf("Unexpected labels on testCase[%v]. Expected: %#v, got: %#v.", i, e, a)
303
-		}
304
-
305
-		// Handle nested Labels
306
-		switch objType := test.obj.(type) {
307
-		case *kapi.ReplicationController:
308
-			if e, a := test.expectedLabels, objType.Spec.Template.Labels; !reflect.DeepEqual(e, a) {
309
-				t.Errorf("Unexpected labels on testCase[%v]. Expected: %#v, got: %#v.", i, e, a)
310
-			}
311
-			if e, a := test.expectedLabels, objType.Spec.Selector; !reflect.DeepEqual(e, a) {
312
-				t.Errorf("Unexpected labels on testCase[%v]. Expected: %#v, got: %#v.", i, e, a)
313
-			}
314
-		case *deployapi.Deployment:
315
-			if e, a := test.expectedLabels, objType.ControllerTemplate.Template.Labels; !reflect.DeepEqual(e, a) {
316
-				t.Errorf("Unexpected labels on testCase[%v]. Expected: %#v, got: %#v.", i, e, a)
317
-			}
318
-		case *deployapi.DeploymentConfig:
319
-			if e, a := test.expectedLabels, objType.Template.ControllerTemplate.Template.Labels; !reflect.DeepEqual(e, a) {
320
-				t.Errorf("Unexpected labels on testCase[%v]. Expected: %#v, got: %#v.", i, e, a)
321
-			}
322
-		}
323
-	}
324
-}
325 1
deleted file mode 100644
... ...
@@ -1,32 +0,0 @@
1
-package config
2
-
3
-import (
4
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
5
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
6
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
7
-	"github.com/openshift/origin/pkg/api/latest"
8
-)
9
-
10
-// Set the default RESTMapper and ObjectTyper
11
-var (
12
-	defaultMapper = latest.RESTMapper
13
-	defaultTyper  = api.Scheme
14
-)
15
-
16
-// DecodeDataToObject decodes the JSON/YAML content into the runtime Object
17
-// using the RESTMapper interface.
18
-// The RESTMapper mappings are returned for the future encoding.
19
-func DecodeDataToObject(data []byte) (obj runtime.Object, mapping *meta.RESTMapping, err error) {
20
-	version, kind, err := defaultTyper.DataVersionAndKind(data)
21
-	if err != nil {
22
-		return
23
-	}
24
-
25
-	mapping, err = defaultMapper.RESTMapping(kind, version)
26
-	if err != nil {
27
-		return
28
-	}
29
-
30
-	obj, err = mapping.Codec.Decode(data)
31
-	return
32
-}
... ...
@@ -10,11 +10,8 @@ type Template struct {
10 10
 	kapi.TypeMeta   `json:",inline"`
11 11
 	kapi.ObjectMeta `json:"metadata,omitempty"`
12 12
 
13
-	// Required: Items is an array of Kubernetes resources of Service,
14
-	// Pod and/or ReplicationController kind.
15
-	// TODO: Handle unregistered types. Define custom []runtime.Object
16
-	//       type and its unmarshaller instead of []runtime.Object.
17
-	Items []runtime.RawExtension `json:"items"`
13
+	// Required: A list of resources that might reference parameters
14
+	Items []runtime.Object `json:"items"`
18 15
 
19 16
 	// Optional: Parameters is an array of Parameters used during the
20 17
 	// Template to Config transformation.
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
9 9
 	"github.com/openshift/origin/pkg/template/api"
10
+	"github.com/openshift/origin/pkg/util"
10 11
 )
11 12
 
12 13
 var parameterNameExp = regexp.MustCompile(`^[a-zA-Z0-9\_]+$`)
... ...
@@ -26,36 +27,15 @@ func ValidateParameter(param *api.Parameter) (errs errors.ValidationErrorList) {
26 26
 // ValidateTemplate tests if required fields in the Template are set.
27 27
 func ValidateTemplate(template *api.Template) (errs errors.ValidationErrorList) {
28 28
 	if len(template.Name) == 0 {
29
-		errs = append(errs, errors.NewFieldRequired("name", template.ObjectMeta.Name))
29
+		errs = append(errs, errors.NewFieldRequired("name", template.Name))
30 30
 	}
31
-	// TODO: Validation of items are now broken as we need to use Typer and Mapper
32
-	//			 parse the proper version/kind and then validate.
33
-	/*
34
-		for i, item := range template.Items {
35
-			err := errors.ValidationErrorList{}
36
-			switch obj := item.Object.(type) {
37
-			case *kapi.ReplicationController:
38
-				err = validation.ValidateReplicationController(obj)
39
-			case *kapi.Pod:
40
-				err = validation.ValidatePod(obj)
41
-			// TODO: ValidateService() now requires registry and context, we should
42
-			// provide them here
43
-			//case *kapi.Service:
44
-			//	err = validation.ValidateService(obj)
45
-			case *routeapi.Route:
46
-				err = routevalidation.ValidateRoute(obj)
47
-			default:
48
-				// Pass-through unknown types.
49
-			}
50
-			// ignore namespace validation errors in templates
51
-			err = filter(err, "namespace")
52
-			errs = append(errs, err.PrefixIndex(i).Prefix("items")...)
53
-		}
54
-	*/
55 31
 	for i := range template.Parameters {
56 32
 		paramErr := ValidateParameter(&template.Parameters[i])
57 33
 		errs = append(errs, paramErr.PrefixIndex(i).Prefix("parameters")...)
58 34
 	}
35
+	for _, obj := range template.Items {
36
+		errs = append(errs, util.ValidateObject(obj)...)
37
+	}
59 38
 	return
60 39
 }
61 40
 
... ...
@@ -82,7 +82,7 @@ func TestValidateTemplate(t *testing.T) {
82 82
 				Parameters: []api.Parameter{
83 83
 					*(makeParameter("VALID_NAME", "1")),
84 84
 				},
85
-				Items: []runtime.RawExtension{},
85
+				Items: []runtime.Object{},
86 86
 			},
87 87
 			true,
88 88
 		},
89 89
new file mode 100644
... ...
@@ -0,0 +1,163 @@
0
+package template
1
+
2
+import (
3
+	"fmt"
4
+	"reflect"
5
+
6
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
+	errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8
+	kmeta "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
11
+	configapi "github.com/openshift/origin/pkg/config/api"
12
+	deployapi "github.com/openshift/origin/pkg/deploy/api"
13
+)
14
+
15
+// MergeInto flags
16
+const (
17
+	OverwriteExistingDstKey = 1 << iota
18
+	ErrorOnExistingDstKey
19
+	ErrorOnDifferentDstKeyValue
20
+)
21
+
22
+// addReplicationControllerNestedLabels adds new label(s) to a nested labels of a single ReplicationController object
23
+func addReplicationControllerNestedLabels(obj *kapi.ReplicationController, labels labels.Set) error {
24
+	if obj.Spec.Template.Labels == nil {
25
+		obj.Spec.Template.Labels = make(map[string]string)
26
+	}
27
+	if err := MergeInto(obj.Spec.Template.Labels, labels, ErrorOnDifferentDstKeyValue); err != nil {
28
+		return fmt.Errorf("unable to add labels to Template.ReplicationController.Spec.Template: %v", err)
29
+	}
30
+	if err := MergeInto(obj.Spec.Template.Labels, obj.Spec.Selector, ErrorOnDifferentDstKeyValue); err != nil {
31
+		return fmt.Errorf("unable to add labels to Template.ReplicationController.Spec.Template: %v", err)
32
+	}
33
+	// Selector and Spec.Template.Labels must be equal
34
+	if obj.Spec.Selector == nil {
35
+		obj.Spec.Selector = make(map[string]string)
36
+	}
37
+	if err := MergeInto(obj.Spec.Selector, obj.Spec.Template.Labels, ErrorOnDifferentDstKeyValue); err != nil {
38
+		return fmt.Errorf("unable to add labels to Template.ReplicationController.Spec.Selector: %v", err)
39
+	}
40
+	return nil
41
+}
42
+
43
+// addDeploymentNestedLabels adds new label(s) to a nested labels of a single Deployment object
44
+func addDeploymentNestedLabels(obj *deployapi.Deployment, labels labels.Set) error {
45
+	if obj.ControllerTemplate.Template.Labels == nil {
46
+		obj.ControllerTemplate.Template.Labels = make(map[string]string)
47
+	}
48
+	if err := MergeInto(obj.ControllerTemplate.Template.Labels, labels, ErrorOnDifferentDstKeyValue); err != nil {
49
+		return fmt.Errorf("unable to add labels to Template.Deployment.ControllerTemplate.Template: %v", err)
50
+	}
51
+	return nil
52
+}
53
+
54
+// addDeploymentConfigNestedLabels adds new label(s) to a nested labels of a single DeploymentConfig object
55
+func addDeploymentConfigNestedLabels(obj *deployapi.DeploymentConfig, labels labels.Set) error {
56
+	if obj.Template.ControllerTemplate.Template.Labels == nil {
57
+		obj.Template.ControllerTemplate.Template.Labels = make(map[string]string)
58
+	}
59
+	if err := MergeInto(obj.Template.ControllerTemplate.Template.Labels, labels, ErrorOnDifferentDstKeyValue); err != nil {
60
+		return fmt.Errorf("unable to add labels to Template.DeploymentConfig.Template.ControllerTemplate.Template: %v", err)
61
+	}
62
+	return nil
63
+}
64
+
65
+// AddObjectLabels adds new label(s) to a single runtime.Object
66
+func AddObjectLabels(obj runtime.Object, labels labels.Set) error {
67
+	if labels == nil {
68
+		// Nothing to add
69
+		return nil
70
+	}
71
+
72
+	accessor, err := kmeta.Accessor(obj)
73
+	if err != nil {
74
+		return err
75
+	}
76
+
77
+	metaLabels := accessor.Labels()
78
+	if metaLabels == nil {
79
+		metaLabels = make(map[string]string)
80
+	}
81
+
82
+	if err := MergeInto(metaLabels, labels, ErrorOnDifferentDstKeyValue); err != nil {
83
+		return fmt.Errorf("unable to add labels to Template.%s: %v", accessor.Kind(), err)
84
+	}
85
+	accessor.SetLabels(metaLabels)
86
+
87
+	// Handle nested Labels
88
+	switch objType := obj.(type) {
89
+	case *kapi.ReplicationController:
90
+		return addReplicationControllerNestedLabels(objType, labels)
91
+	case *deployapi.Deployment:
92
+		return addDeploymentNestedLabels(objType, labels)
93
+	case *deployapi.DeploymentConfig:
94
+		return addDeploymentConfigNestedLabels(objType, labels)
95
+	}
96
+
97
+	return nil
98
+}
99
+
100
+// AddConfigLabels adds new label(s) to all resources defined in the given Config.
101
+func AddConfigLabels(c *configapi.Config, labels labels.Set) errs.ValidationErrorList {
102
+	itemErrors := errs.ValidationErrorList{}
103
+	for i, in := range c.Items {
104
+		if err := AddObjectLabels(in, labels); err != nil {
105
+			reportError(&itemErrors, i, *errs.NewFieldInvalid("labels", err, fmt.Sprintf("error applying labels %v to %v", labels, in)))
106
+		}
107
+	}
108
+	return itemErrors.Prefix("Config")
109
+}
110
+
111
+// MergeInto merges items from a src map into a dst map.
112
+// Returns an error when the maps are not of the same type.
113
+// Flags:
114
+// - ErrorOnExistingDstKey
115
+//     When set: Return an error if any of the dst keys is already set.
116
+// - ErrorOnDifferentDstKeyValue
117
+//     When set: Return an error if any of the dst keys is already set
118
+//               to a different value than src key.
119
+// - OverwriteDstKey
120
+//     When set: Overwrite existing dst key value with src key value.
121
+func MergeInto(dst, src interface{}, flags int) error {
122
+	dstVal := reflect.ValueOf(dst)
123
+	srcVal := reflect.ValueOf(src)
124
+
125
+	if dstVal.Kind() != reflect.Map {
126
+		return fmt.Errorf("dst is not a valid map: %v", dstVal.Kind())
127
+	}
128
+	if srcVal.Kind() != reflect.Map {
129
+		return fmt.Errorf("src is not a valid map: %v", srcVal.Kind())
130
+	}
131
+	if dstTyp, srcTyp := dstVal.Type(), srcVal.Type(); !dstTyp.AssignableTo(srcTyp) {
132
+		return fmt.Errorf("type mismatch, can't assign '%v' to '%v'", srcTyp, dstTyp)
133
+	}
134
+
135
+	if dstVal.IsNil() {
136
+		return fmt.Errorf("dst value is nil")
137
+	}
138
+	if srcVal.IsNil() {
139
+		// Nothing to merge
140
+		return nil
141
+	}
142
+
143
+	for _, k := range srcVal.MapKeys() {
144
+		if dstVal.MapIndex(k).IsValid() {
145
+			if flags&ErrorOnExistingDstKey != 0 {
146
+				return fmt.Errorf("dst key already set (ErrorOnExistingDstKey=1), '%v'='%v'", k, dstVal.MapIndex(k))
147
+			}
148
+			if dstVal.MapIndex(k).String() != srcVal.MapIndex(k).String() {
149
+				if flags&ErrorOnDifferentDstKeyValue != 0 {
150
+					return fmt.Errorf("dst key already set to a different value (ErrorOnDifferentDstKeyValue=1), '%v'='%v'", k, dstVal.MapIndex(k))
151
+				}
152
+				if flags&OverwriteExistingDstKey != 0 {
153
+					dstVal.SetMapIndex(k, srcVal.MapIndex(k))
154
+				}
155
+			}
156
+		} else {
157
+			dstVal.SetMapIndex(k, srcVal.MapIndex(k))
158
+		}
159
+	}
160
+
161
+	return nil
162
+}
0 163
new file mode 100644
... ...
@@ -0,0 +1,332 @@
0
+package template
1
+
2
+import (
3
+	"reflect"
4
+	"testing"
5
+
6
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
+	kmeta "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
9
+	deployapi "github.com/openshift/origin/pkg/deploy/api"
10
+)
11
+
12
+type FakeLabelsResource struct {
13
+	kapi.TypeMeta   `json:",inline"`
14
+	kapi.ObjectMeta `json:"metadata,omitempty"`
15
+}
16
+
17
+func (*FakeLabelsResource) IsAnAPIObject() {}
18
+
19
+func TestAddConfigLabels(t *testing.T) {
20
+	var nilLabels map[string]string
21
+
22
+	testCases := []struct {
23
+		obj            runtime.Object
24
+		addLabels      map[string]string
25
+		err            bool
26
+		expectedLabels map[string]string
27
+	}{
28
+		{ // [0] Test nil + nil => nil
29
+			obj:            &kapi.Pod{},
30
+			addLabels:      nilLabels,
31
+			err:            false,
32
+			expectedLabels: nilLabels,
33
+		},
34
+		{ // [1] Test nil + empty labels => empty labels
35
+			obj:            &kapi.Pod{},
36
+			addLabels:      map[string]string{},
37
+			err:            false,
38
+			expectedLabels: map[string]string{},
39
+		},
40
+		{ // [2] Test obj.Labels + nil => obj.Labels
41
+			obj: &kapi.Pod{
42
+				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
43
+			},
44
+			addLabels:      nilLabels,
45
+			err:            false,
46
+			expectedLabels: map[string]string{"foo": "bar"},
47
+		},
48
+		{ // [3] Test obj.Labels + empty labels => obj.Labels
49
+			obj: &kapi.Pod{
50
+				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
51
+			},
52
+			addLabels:      map[string]string{},
53
+			err:            false,
54
+			expectedLabels: map[string]string{"foo": "bar"},
55
+		},
56
+		{ // [4] Test nil + addLabels => addLabels
57
+			obj:            &kapi.Pod{},
58
+			addLabels:      map[string]string{"foo": "bar"},
59
+			err:            false,
60
+			expectedLabels: map[string]string{"foo": "bar"},
61
+		},
62
+		{ // [5] Test obj.labels + addLabels => expectedLabels
63
+			obj: &kapi.Service{
64
+				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"baz": ""}},
65
+			},
66
+			addLabels:      map[string]string{"foo": "bar"},
67
+			err:            false,
68
+			expectedLabels: map[string]string{"foo": "bar", "baz": ""},
69
+		},
70
+		{ // [6] Test conflicting keys with the same value
71
+			obj: &kapi.Service{
72
+				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"foo": "same value"}},
73
+			},
74
+			addLabels:      map[string]string{"foo": "same value"},
75
+			err:            false,
76
+			expectedLabels: map[string]string{"foo": "same value"},
77
+		},
78
+		{ // [7] Test conflicting keys with a different value
79
+			obj: &kapi.Service{
80
+				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"foo": "first value"}},
81
+			},
82
+			addLabels:      map[string]string{"foo": "second value"},
83
+			err:            true,
84
+			expectedLabels: map[string]string{"foo": "first value"},
85
+		},
86
+		{ // [8] Test conflicting keys with the same value in ReplicationController nested labels
87
+			obj: &kapi.ReplicationController{
88
+				ObjectMeta: kapi.ObjectMeta{
89
+					Labels: map[string]string{"foo": "same value"},
90
+				},
91
+				Spec: kapi.ReplicationControllerSpec{
92
+					Template: &kapi.PodTemplateSpec{
93
+						ObjectMeta: kapi.ObjectMeta{
94
+							Labels: map[string]string{"foo": "same value"},
95
+						},
96
+					},
97
+				},
98
+			},
99
+			addLabels:      map[string]string{"foo": "same value"},
100
+			err:            false,
101
+			expectedLabels: map[string]string{"foo": "same value"},
102
+		},
103
+		{ // [9] Test conflicting keys with a different value in ReplicationController nested labels
104
+			obj: &kapi.ReplicationController{
105
+				ObjectMeta: kapi.ObjectMeta{
106
+					Labels: map[string]string{"foo": "bar"},
107
+				},
108
+				Spec: kapi.ReplicationControllerSpec{
109
+					Template: &kapi.PodTemplateSpec{
110
+						ObjectMeta: kapi.ObjectMeta{
111
+							Labels: map[string]string{"foo": "bar"},
112
+						},
113
+					},
114
+					Selector: map[string]string{"foo": "bar"},
115
+				},
116
+			},
117
+			addLabels:      map[string]string{"baz": ""},
118
+			err:            false,
119
+			expectedLabels: map[string]string{"foo": "bar", "baz": ""},
120
+		},
121
+		{ // [10] Test conflicting keys with a different value in ReplicationController nested labels
122
+			obj: &kapi.ReplicationController{
123
+				ObjectMeta: kapi.ObjectMeta{
124
+					Labels: map[string]string{"foo": "first value"},
125
+				},
126
+				Spec: kapi.ReplicationControllerSpec{
127
+					Template: &kapi.PodTemplateSpec{
128
+						ObjectMeta: kapi.ObjectMeta{
129
+							Labels: map[string]string{"foo": "first value"},
130
+						},
131
+					},
132
+					Selector: map[string]string{"foo": "first value"},
133
+				},
134
+			},
135
+			addLabels:      map[string]string{"foo": "second value"},
136
+			err:            true,
137
+			expectedLabels: map[string]string{"foo": "first value"},
138
+		},
139
+		{ // [11] Test adding labels to a Deployment object
140
+			obj: &deployapi.Deployment{
141
+				ObjectMeta: kapi.ObjectMeta{
142
+					Labels: map[string]string{"foo": "first value"},
143
+				},
144
+				ControllerTemplate: kapi.ReplicationControllerSpec{
145
+					Template: &kapi.PodTemplateSpec{
146
+						ObjectMeta: kapi.ObjectMeta{
147
+							Labels: map[string]string{"foo": "first value"},
148
+						},
149
+					},
150
+				},
151
+			},
152
+			addLabels:      map[string]string{"bar": "second value"},
153
+			err:            false,
154
+			expectedLabels: map[string]string{"foo": "first value", "bar": "second value"},
155
+		},
156
+		{ // [12] Test adding labels to a DeploymentConfig object
157
+			obj: &deployapi.DeploymentConfig{
158
+				ObjectMeta: kapi.ObjectMeta{
159
+					Labels: map[string]string{"foo": "first value"},
160
+				},
161
+				Template: deployapi.DeploymentTemplate{
162
+					ControllerTemplate: kapi.ReplicationControllerSpec{
163
+						Template: &kapi.PodTemplateSpec{
164
+							ObjectMeta: kapi.ObjectMeta{
165
+								Labels: map[string]string{"foo": "first value"},
166
+							},
167
+						},
168
+					},
169
+				},
170
+			},
171
+			addLabels:      map[string]string{"bar": "second value"},
172
+			err:            false,
173
+			expectedLabels: map[string]string{"foo": "first value", "bar": "second value"},
174
+		},
175
+		{ // [13] Test unknown Generic Object with Labels field
176
+			obj: &FakeLabelsResource{
177
+				ObjectMeta: kapi.ObjectMeta{Labels: map[string]string{"baz": ""}},
178
+			},
179
+			addLabels:      map[string]string{"foo": "bar"},
180
+			err:            false,
181
+			expectedLabels: map[string]string{"foo": "bar", "baz": ""},
182
+		},
183
+	}
184
+
185
+	for i, test := range testCases {
186
+		err := AddObjectLabels(test.obj, test.addLabels)
187
+		if err != nil && !test.err {
188
+			t.Errorf("Unexpected error while setting labels on testCase[%v]: %v.", i, err)
189
+		} else if err == nil && test.err {
190
+			t.Errorf("Unexpected non-error while setting labels on testCase[%v].", i)
191
+		}
192
+
193
+		accessor, err := kmeta.Accessor(test.obj)
194
+		if err != nil {
195
+			t.Error(err)
196
+		}
197
+		metaLabels := accessor.Labels()
198
+		if e, a := test.expectedLabels, metaLabels; !reflect.DeepEqual(e, a) {
199
+			t.Errorf("Unexpected labels on testCase[%v]. Expected: %#v, got: %#v.", i, e, a)
200
+		}
201
+
202
+		// Handle nested Labels
203
+		switch objType := test.obj.(type) {
204
+		case *kapi.ReplicationController:
205
+			if e, a := test.expectedLabels, objType.Spec.Template.Labels; !reflect.DeepEqual(e, a) {
206
+				t.Errorf("Unexpected labels on testCase[%v]. Expected: %#v, got: %#v.", i, e, a)
207
+			}
208
+			if e, a := test.expectedLabels, objType.Spec.Selector; !reflect.DeepEqual(e, a) {
209
+				t.Errorf("Unexpected labels on testCase[%v]. Expected: %#v, got: %#v.", i, e, a)
210
+			}
211
+		case *deployapi.Deployment:
212
+			if e, a := test.expectedLabels, objType.ControllerTemplate.Template.Labels; !reflect.DeepEqual(e, a) {
213
+				t.Errorf("Unexpected labels on testCase[%v]. Expected: %#v, got: %#v.", i, e, a)
214
+			}
215
+		case *deployapi.DeploymentConfig:
216
+			if e, a := test.expectedLabels, objType.Template.ControllerTemplate.Template.Labels; !reflect.DeepEqual(e, a) {
217
+				t.Errorf("Unexpected labels on testCase[%v]. Expected: %#v, got: %#v.", i, e, a)
218
+			}
219
+		}
220
+	}
221
+}
222
+
223
+func TestMergeInto(t *testing.T) {
224
+	var nilMap map[int]int
225
+
226
+	testCases := []struct {
227
+		dst      interface{}
228
+		src      interface{}
229
+		flags    int
230
+		err      bool
231
+		expected interface{}
232
+	}{
233
+		{ // [0] Can't merge into nil
234
+			dst:      nil,
235
+			src:      map[int]int{},
236
+			flags:    0,
237
+			err:      true,
238
+			expected: nil,
239
+		},
240
+		{ // [1] Can't merge untyped nil into an empty map
241
+			dst:      map[int]int{},
242
+			src:      nil,
243
+			flags:    0,
244
+			err:      true,
245
+			expected: map[int]int{},
246
+		},
247
+		{ // [2] Merge nil map into an empty map
248
+			dst:      map[int]int{},
249
+			src:      nilMap,
250
+			flags:    0,
251
+			err:      false,
252
+			expected: map[int]int{},
253
+		},
254
+		{ // [3] Can't merge into nil map
255
+			dst:      nilMap,
256
+			src:      map[int]int{},
257
+			flags:    0,
258
+			err:      true,
259
+			expected: nilMap,
260
+		},
261
+		{ // [4] Can't merge into pointer
262
+			dst:      &nilMap,
263
+			src:      map[int]int{},
264
+			flags:    0,
265
+			err:      true,
266
+			expected: &nilMap,
267
+		},
268
+		{ // [5] Test empty maps
269
+			dst:      map[int]int{},
270
+			src:      map[int]int{},
271
+			flags:    0,
272
+			err:      false,
273
+			expected: map[int]int{},
274
+		},
275
+		{ // [6] Test dst + src => expected
276
+			dst:      map[int]byte{0: 0, 1: 1},
277
+			src:      map[int]byte{2: 2, 3: 3},
278
+			flags:    0,
279
+			err:      false,
280
+			expected: map[int]byte{0: 0, 1: 1, 2: 2, 3: 3},
281
+		},
282
+		{ // [7] Test dst + src => expected, do not overwrite dst
283
+			dst:      map[string]string{"foo": "bar"},
284
+			src:      map[string]string{"foo": ""},
285
+			flags:    0,
286
+			err:      false,
287
+			expected: map[string]string{"foo": "bar"},
288
+		},
289
+		{ // [8] Test dst + src => expected, overwrite dst
290
+			dst:      map[string]string{"foo": "bar"},
291
+			src:      map[string]string{"foo": ""},
292
+			flags:    OverwriteExistingDstKey,
293
+			err:      false,
294
+			expected: map[string]string{"foo": ""},
295
+		},
296
+		{ // [9] Test dst + src => expected, error on existing key value
297
+			dst:      map[string]string{"foo": "bar"},
298
+			src:      map[string]string{"foo": "bar"},
299
+			flags:    ErrorOnExistingDstKey | OverwriteExistingDstKey,
300
+			err:      true,
301
+			expected: map[string]string{"foo": "bar"},
302
+		},
303
+		{ // [10] Test dst + src => expected, do not error on same key value
304
+			dst:      map[string]string{"foo": "bar"},
305
+			src:      map[string]string{"foo": "bar"},
306
+			flags:    ErrorOnDifferentDstKeyValue | OverwriteExistingDstKey,
307
+			err:      false,
308
+			expected: map[string]string{"foo": "bar"},
309
+		},
310
+		{ // [11] Test dst + src => expected, error on different key value
311
+			dst:      map[string]string{"foo": "bar"},
312
+			src:      map[string]string{"foo": ""},
313
+			flags:    ErrorOnDifferentDstKeyValue | OverwriteExistingDstKey,
314
+			err:      true,
315
+			expected: map[string]string{"foo": "bar"},
316
+		},
317
+	}
318
+
319
+	for i, test := range testCases {
320
+		err := MergeInto(test.dst, test.src, test.flags)
321
+		if err != nil && !test.err {
322
+			t.Errorf("Unexpected error while merging maps on testCase[%v]: %v.", i, err)
323
+		} else if err == nil && test.err {
324
+			t.Errorf("Unexpected non-error while merging maps on testCase[%v].", i)
325
+		}
326
+
327
+		if !reflect.DeepEqual(test.dst, test.expected) {
328
+			t.Errorf("Unexpected map on testCase[%v]. Expected: %#v, got: %#v.", i, test.expected, test.dst)
329
+		}
330
+	}
331
+}
... ...
@@ -12,8 +12,6 @@ import (
12 12
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
13 13
 	utilerr "github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
14 14
 	"github.com/golang/glog"
15
-
16
-	"github.com/openshift/origin/pkg/config"
17 15
 	"github.com/openshift/origin/pkg/template"
18 16
 	"github.com/openshift/origin/pkg/template/api"
19 17
 	"github.com/openshift/origin/pkg/template/api/validation"
... ...
@@ -49,10 +47,6 @@ func (s *REST) Create(ctx kapi.Context, obj runtime.Object) (<-chan apiserver.RE
49 49
 	if !ok {
50 50
 		return nil, apierr.NewBadRequest("not a template")
51 51
 	}
52
-	if len(tpl.Name) == 0 {
53
-		tpl.Name = "default"
54
-	}
55
-	kapi.FillObjectMetaSystemFields(ctx, &tpl.ObjectMeta)
56 52
 	if errs := validation.ValidateTemplate(tpl); len(errs) > 0 {
57 53
 		return nil, apierr.NewInvalid("template", tpl.Name, errs)
58 54
 	}
... ...
@@ -68,7 +62,7 @@ func (s *REST) Create(ctx kapi.Context, obj runtime.Object) (<-chan apiserver.RE
68 68
 			glog.V(1).Infof(utilerr.NewAggregate(err).Error())
69 69
 		}
70 70
 
71
-		if err := config.AddConfigLabels(cfg, labels.Set{"template": tpl.Name}); len(err) > 0 {
71
+		if err := template.AddConfigLabels(cfg, labels.Set{"template": tpl.Name}); len(err) > 0 {
72 72
 			// TODO: We don't report the processing errors to users as there is no
73 73
 			// good way how to do it for just some items.
74 74
 			glog.V(1).Infof(utilerr.NewAggregate(err).Error())
... ...
@@ -19,18 +19,19 @@ func TestNewRESTInvalidType(t *testing.T) {
19 19
 
20 20
 func TestNewRESTDefaultsName(t *testing.T) {
21 21
 	storage := NewREST()
22
-	ch, err := storage.Create(nil, &template.Template{})
22
+	ch, err := storage.Create(nil, &template.Template{
23
+		ObjectMeta: kapi.ObjectMeta{
24
+			Name: "test",
25
+		},
26
+	})
23 27
 	if err != nil {
24 28
 		t.Fatalf("unexpected error: %v", err)
25 29
 	}
26 30
 	obj := <-ch
27
-	config, ok := obj.Object.(*config.Config)
31
+	_, ok := obj.Object.(*config.Config)
28 32
 	if !ok {
29 33
 		t.Fatalf("unexpected return object: %#v", obj)
30 34
 	}
31
-	if len(config.Name) == 0 {
32
-		t.Errorf("expected name to be set")
33
-	}
34 35
 }
35 36
 
36 37
 func TestStorageNotImplementedFunctions(t *testing.T) {
... ...
@@ -7,9 +7,8 @@ import (
7 7
 
8 8
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
9 9
 	errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
10 11
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
11
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
12
-	"github.com/openshift/origin/pkg/config"
13 12
 	configapi "github.com/openshift/origin/pkg/config/api"
14 13
 	deployapi "github.com/openshift/origin/pkg/deploy/api"
15 14
 	"github.com/openshift/origin/pkg/template/api"
... ...
@@ -18,26 +17,24 @@ import (
18 18
 
19 19
 var parameterExp = regexp.MustCompile(`\$\{([a-zA-Z0-9\_]+)\}`)
20 20
 
21
-// Processor transforms Template objects into Config objects.
21
+// reportError reports the single item validation error and properly set the
22
+// prefix and index to match the Config item JSON index
23
+func reportError(allErrs *errs.ValidationErrorList, index int, err errs.ValidationError) {
24
+	i := errs.ValidationErrorList{}
25
+	*allErrs = append(*allErrs, append(i, &err).PrefixIndex(index).Prefix("item")...)
26
+}
27
+
28
+// Processor process the Template into the List with substituted parameters
22 29
 type Processor struct {
23 30
 	Generators map[string]Generator
24 31
 }
25 32
 
26
-// NewProcessor creates new Processor and initializes
27
-// its set of generators.
33
+// NewProcessor creates new Processor and initializes its set of generators.
28 34
 func NewProcessor(generators map[string]Generator) *Processor {
29 35
 	return &Processor{Generators: generators}
30 36
 }
31 37
 
32
-// reportError reports the single item validation error and properly set the
33
-// prefix and index to match the Config item JSON index
34
-// TODO: Move this into some 'utils' or 'helpers' package
35
-func reportError(allErrs *errs.ValidationErrorList, index int, err errs.ValidationError) {
36
-	i := errs.ValidationErrorList{}
37
-	*allErrs = append(*allErrs, append(i, &err).PrefixIndex(index).Prefix("item")...)
38
-}
39
-
40
-// Process transforms Template object into Config object. It generates
38
+// Process transforms Template object into List object. It generates
41 39
 // Parameter values using the defined set of generators first, and then it
42 40
 // substitutes all Parameter expression occurances with their corresponding
43 41
 // values (currently in the containers' Environment variables only).
... ...
@@ -48,38 +45,21 @@ func (p *Processor) Process(template *api.Template) (*configapi.Config, errs.Val
48 48
 		return nil, append(templateErrors.Prefix("Template"), errs.NewFieldInvalid("parameters", err, "failure to generate parameter value"))
49 49
 	}
50 50
 
51
-	items := []runtime.RawExtension{}
52
-	for i, in := range template.Items {
53
-
54
-		item, mapping, err := config.DecodeDataToObject(in.RawJSON)
55
-		if err != nil {
56
-			reportError(&templateErrors, i, *errs.NewFieldInvalid("decode", err, fmt.Sprintf("decoding failure for %v", in)))
57
-			continue
58
-		}
59
-
60
-		// TODO: Report failed parameter substitution for unknown objects
61
-		//			 These errors are no-fatal and the object returned is the same
62
-		//			 just without substitution.
51
+	for i, item := range template.Items {
63 52
 		newItem, err := p.SubstituteParameters(template.Parameters, item)
64 53
 		if err != nil {
65 54
 			reportError(&templateErrors, i, *errs.NewFieldNotSupported("parameters", err))
66 55
 		}
67
-
68
-		jsonItem, err := mapping.Codec.Encode(newItem)
56
+		// Remove namespace from the item
57
+		itemMeta, err := meta.Accessor(newItem)
69 58
 		if err != nil {
70
-			reportError(&templateErrors, i, *errs.NewFieldInvalid("decode", err, fmt.Sprintf("decoding failure for %v", newItem)))
71
-			continue
59
+			reportError(&templateErrors, i, *errs.NewFieldInvalid("namespace", err, "failed to remove the item namespace"))
72 60
 		}
73
-		items = append(items, runtime.RawExtension{RawJSON: jsonItem})
61
+		itemMeta.SetNamespace("")
62
+		template.Items[i] = newItem
74 63
 	}
75 64
 
76
-	config := &configapi.Config{
77
-		ObjectMeta: template.ObjectMeta,
78
-		Items:      items,
79
-	}
80
-	config.Kind = "Config"
81
-	config.ObjectMeta.CreationTimestamp = util.Now()
82
-	return config, templateErrors.Prefix("Template")
65
+	return &configapi.Config{Items: template.Items}, templateErrors.Prefix("Template")
83 66
 }
84 67
 
85 68
 // AddParameter adds new custom parameter to the Template. It overrides
... ...
@@ -6,10 +6,8 @@ import (
6 6
 	"io/ioutil"
7 7
 	"math/rand"
8 8
 	"testing"
9
-	"time"
10 9
 
11 10
 	_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
12
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
13 11
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
14 12
 	"github.com/openshift/origin/pkg/api/latest"
15 13
 	"github.com/openshift/origin/pkg/template/api"
... ...
@@ -24,15 +22,6 @@ func makeParameter(name, value, generate string) api.Parameter {
24 24
 	}
25 25
 }
26 26
 
27
-func TestNewTemplate(t *testing.T) {
28
-	var template api.Template
29
-
30
-	jsonData, _ := ioutil.ReadFile("../../examples/guestbook/template.json")
31
-	if err := json.Unmarshal(jsonData, &template); err != nil {
32
-		t.Errorf("Unable to process the JSON template file: %v", err)
33
-	}
34
-}
35
-
36 27
 func TestAddParameter(t *testing.T) {
37 28
 	var template api.Template
38 29
 
... ...
@@ -132,9 +121,6 @@ func ExampleProcessTemplateParameters() {
132 132
 	config, err := processor.Process(&template)
133 133
 	fmt.Println(errors.NewAggregate(err))
134 134
 	if config != nil {
135
-		// Reset the timestamp for the output comparison
136
-		config.ObjectMeta.CreationTimestamp = util.Date(1980, 1, 1, 0, 0, 0, 0, time.UTC)
137
-
138 135
 		result, err := latest.Codec.Encode(config)
139 136
 		if err != nil {
140 137
 			fmt.Printf("Unexpected error during encoding Config: %#v", err)
... ...
@@ -143,5 +129,5 @@ func ExampleProcessTemplateParameters() {
143 143
 	}
144 144
 	// Output:
145 145
 	//<nil>
146
-	//{"kind":"Config","apiVersion":"v1beta1","metadata":{"name":"guestbook-example","creationTimestamp":"1980-01-01T00:00:00Z","annotations":{"description":"Example shows how to build a simple multi-tier application using Kubernetes and Docker"}},"items":[{"kind":"Route","apiVersion":"v1beta1","metadata":{"creationTimestamp":null},"host":"guestbook.example.com","serviceName":"frontend"},{"kind":"Service","id":"frontend","creationTimestamp":null,"apiVersion":"v1beta1","port":5432,"selector":{"name":"guestbook"},"containerPort":0},{"kind":"Service","id":"redis-master","creationTimestamp":null,"apiVersion":"v1beta1","port":10000,"selector":{"name":"redis-master"},"containerPort":0},{"kind":"Service","id":"redis-slave","creationTimestamp":null,"apiVersion":"v1beta1","port":10001,"selector":{"name":"redis-slave"},"containerPort":0},{"kind":"Pod","id":"redis-master","creationTimestamp":null,"apiVersion":"v1beta1","labels":{"name":"redis-master"},"desiredState":{"manifest":{"version":"v1beta2","id":"","volumes":null,"containers":[{"name":"master","image":"dockerfile/redis","ports":[{"containerPort":6379}],"env":[{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"currentState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}},{"kind":"ReplicationController","id":"guestbook","creationTimestamp":null,"apiVersion":"v1beta1","desiredState":{"replicas":3,"replicaSelector":{"name":"frontend"},"podTemplate":{"desiredState":{"manifest":{"version":"v1beta2","id":"","volumes":null,"containers":[{"name":"php-redis","image":"brendanburns/php-redis","ports":[{"hostPort":8000,"containerPort":80}],"env":[{"name":"ADMIN_USERNAME","key":"ADMIN_USERNAME","value":"adminQ3H"},{"name":"ADMIN_PASSWORD","key":"ADMIN_PASSWORD","value":"dwNJiJwW"},{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"labels":{"name":"frontend"}}},"currentState":{"replicas":0,"podTemplate":{"desiredState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}}}},{"kind":"ReplicationController","id":"redis-slave","creationTimestamp":null,"apiVersion":"v1beta1","desiredState":{"replicas":2,"replicaSelector":{"name":"redis-slave"},"podTemplate":{"desiredState":{"manifest":{"version":"v1beta2","id":"","volumes":null,"containers":[{"name":"slave","image":"brendanburns/redis-slave","ports":[{"hostPort":6380,"containerPort":6379}],"env":[{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"labels":{"name":"redis-slave"}}},"currentState":{"replicas":0,"podTemplate":{"desiredState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}}}}]}
146
+	//{"kind":"Config","apiVersion":"v1beta1","metadata":{"creationTimestamp":null},"items":[{"kind":"Route","apiVersion":"v1beta1","metadata":{"creationTimestamp":null},"host":"guestbook.example.com","serviceName":"frontend-service"},{"kind":"Service","id":"frontend-service","creationTimestamp":null,"apiVersion":"v1beta1","port":5432,"selector":{"name":"guestbook"},"containerPort":0},{"kind":"Service","id":"redis-master","creationTimestamp":null,"apiVersion":"v1beta1","port":10000,"selector":{"name":"redis-master"},"containerPort":0},{"kind":"Service","id":"redis-slave","creationTimestamp":null,"apiVersion":"v1beta1","port":10001,"selector":{"name":"redis-slave"},"containerPort":0},{"kind":"Pod","id":"redis-master","creationTimestamp":null,"apiVersion":"v1beta1","labels":{"name":"redis-master"},"desiredState":{"manifest":{"version":"v1beta2","id":"","volumes":null,"containers":[{"name":"master","image":"dockerfile/redis","ports":[{"containerPort":6379}],"env":[{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"currentState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}},{"kind":"ReplicationController","id":"guestbook","creationTimestamp":null,"apiVersion":"v1beta1","desiredState":{"replicas":3,"replicaSelector":{"name":"frontend-service"},"podTemplate":{"desiredState":{"manifest":{"version":"v1beta2","id":"","volumes":null,"containers":[{"name":"php-redis","image":"brendanburns/php-redis","ports":[{"hostPort":8000,"containerPort":80}],"env":[{"name":"ADMIN_USERNAME","key":"ADMIN_USERNAME","value":"adminQ3H"},{"name":"ADMIN_PASSWORD","key":"ADMIN_PASSWORD","value":"dwNJiJwW"},{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"labels":{"name":"frontend-service"}}},"currentState":{"replicas":0,"podTemplate":{"desiredState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}}}},{"kind":"ReplicationController","id":"redis-slave","creationTimestamp":null,"apiVersion":"v1beta1","desiredState":{"replicas":2,"replicaSelector":{"name":"redis-slave"},"podTemplate":{"desiredState":{"manifest":{"version":"v1beta2","id":"","volumes":null,"containers":[{"name":"slave","image":"brendanburns/redis-slave","ports":[{"hostPort":6380,"containerPort":6379}],"env":[{"name":"REDIS_PASSWORD","key":"REDIS_PASSWORD","value":"P8vxbV4C"}],"imagePullPolicy":""}],"restartPolicy":{}}},"labels":{"name":"redis-slave"}}},"currentState":{"replicas":0,"podTemplate":{"desiredState":{"manifest":{"version":"","id":"","volumes":null,"containers":null,"restartPolicy":{}}}}}}]}
147 147
 }
148 148
deleted file mode 100644
... ...
@@ -1,66 +0,0 @@
1
-package util
2
-
3
-import (
4
-	"fmt"
5
-	"reflect"
6
-)
7
-
8
-// MergeInto flags
9
-const (
10
-	OverwriteExistingDstKey = 1 << iota
11
-	ErrorOnExistingDstKey
12
-	ErrorOnDifferentDstKeyValue
13
-)
14
-
15
-// MergeInto merges items from a src map into a dst map.
16
-// Returns an error when the maps are not of the same type.
17
-// Flags:
18
-// - ErrorOnExistingDstKey
19
-//     When set: Return an error if any of the dst keys is already set.
20
-// - ErrorOnDifferentDstKeyValue
21
-//     When set: Return an error if any of the dst keys is already set
22
-//               to a different value than src key.
23
-// - OverwriteDstKey
24
-//     When set: Overwrite existing dst key value with src key value.
25
-func MergeInto(dst, src interface{}, flags int) error {
26
-	dstVal := reflect.ValueOf(dst)
27
-	srcVal := reflect.ValueOf(src)
28
-
29
-	if dstVal.Kind() != reflect.Map {
30
-		return fmt.Errorf("dst is not a valid map: %v", dstVal.Kind())
31
-	}
32
-	if srcVal.Kind() != reflect.Map {
33
-		return fmt.Errorf("src is not a valid map: %v", srcVal.Kind())
34
-	}
35
-	if dstTyp, srcTyp := dstVal.Type(), srcVal.Type(); !dstTyp.AssignableTo(srcTyp) {
36
-		return fmt.Errorf("type mismatch, can't assign '%v' to '%v'", srcTyp, dstTyp)
37
-	}
38
-
39
-	if dstVal.IsNil() {
40
-		return fmt.Errorf("dst value is nil")
41
-	}
42
-	if srcVal.IsNil() {
43
-		// Nothing to merge
44
-		return nil
45
-	}
46
-
47
-	for _, k := range srcVal.MapKeys() {
48
-		if dstVal.MapIndex(k).IsValid() {
49
-			if flags&ErrorOnExistingDstKey != 0 {
50
-				return fmt.Errorf("dst key already set (ErrorOnExistingDstKey=1), '%v'='%v'", k, dstVal.MapIndex(k))
51
-			}
52
-			if dstVal.MapIndex(k).String() != srcVal.MapIndex(k).String() {
53
-				if flags&ErrorOnDifferentDstKeyValue != 0 {
54
-					return fmt.Errorf("dst key already set to a different value (ErrorOnDifferentDstKeyValue=1), '%v'='%v'", k, dstVal.MapIndex(k))
55
-				}
56
-				if flags&OverwriteExistingDstKey != 0 {
57
-					dstVal.SetMapIndex(k, srcVal.MapIndex(k))
58
-				}
59
-			}
60
-		} else {
61
-			dstVal.SetMapIndex(k, srcVal.MapIndex(k))
62
-		}
63
-	}
64
-
65
-	return nil
66
-}
67 1
deleted file mode 100644
... ...
@@ -1,116 +0,0 @@
1
-package util
2
-
3
-import (
4
-	"reflect"
5
-	"testing"
6
-)
7
-
8
-func TestMergeInto(t *testing.T) {
9
-	var nilMap map[int]int
10
-
11
-	testCases := []struct {
12
-		dst      interface{}
13
-		src      interface{}
14
-		flags    int
15
-		err      bool
16
-		expected interface{}
17
-	}{
18
-		{ // [0] Can't merge into nil
19
-			dst:      nil,
20
-			src:      map[int]int{},
21
-			flags:    0,
22
-			err:      true,
23
-			expected: nil,
24
-		},
25
-		{ // [1] Can't merge untyped nil into an empty map
26
-			dst:      map[int]int{},
27
-			src:      nil,
28
-			flags:    0,
29
-			err:      true,
30
-			expected: map[int]int{},
31
-		},
32
-		{ // [2] Merge nil map into an empty map
33
-			dst:      map[int]int{},
34
-			src:      nilMap,
35
-			flags:    0,
36
-			err:      false,
37
-			expected: map[int]int{},
38
-		},
39
-		{ // [3] Can't merge into nil map
40
-			dst:      nilMap,
41
-			src:      map[int]int{},
42
-			flags:    0,
43
-			err:      true,
44
-			expected: nilMap,
45
-		},
46
-		{ // [4] Can't merge into pointer
47
-			dst:      &nilMap,
48
-			src:      map[int]int{},
49
-			flags:    0,
50
-			err:      true,
51
-			expected: &nilMap,
52
-		},
53
-		{ // [5] Test empty maps
54
-			dst:      map[int]int{},
55
-			src:      map[int]int{},
56
-			flags:    0,
57
-			err:      false,
58
-			expected: map[int]int{},
59
-		},
60
-		{ // [6] Test dst + src => expected
61
-			dst:      map[int]byte{0: 0, 1: 1},
62
-			src:      map[int]byte{2: 2, 3: 3},
63
-			flags:    0,
64
-			err:      false,
65
-			expected: map[int]byte{0: 0, 1: 1, 2: 2, 3: 3},
66
-		},
67
-		{ // [7] Test dst + src => expected, do not overwrite dst
68
-			dst:      map[string]string{"foo": "bar"},
69
-			src:      map[string]string{"foo": ""},
70
-			flags:    0,
71
-			err:      false,
72
-			expected: map[string]string{"foo": "bar"},
73
-		},
74
-		{ // [8] Test dst + src => expected, overwrite dst
75
-			dst:      map[string]string{"foo": "bar"},
76
-			src:      map[string]string{"foo": ""},
77
-			flags:    OverwriteExistingDstKey,
78
-			err:      false,
79
-			expected: map[string]string{"foo": ""},
80
-		},
81
-		{ // [9] Test dst + src => expected, error on existing key value
82
-			dst:      map[string]string{"foo": "bar"},
83
-			src:      map[string]string{"foo": "bar"},
84
-			flags:    ErrorOnExistingDstKey | OverwriteExistingDstKey,
85
-			err:      true,
86
-			expected: map[string]string{"foo": "bar"},
87
-		},
88
-		{ // [10] Test dst + src => expected, do not error on same key value
89
-			dst:      map[string]string{"foo": "bar"},
90
-			src:      map[string]string{"foo": "bar"},
91
-			flags:    ErrorOnDifferentDstKeyValue | OverwriteExistingDstKey,
92
-			err:      false,
93
-			expected: map[string]string{"foo": "bar"},
94
-		},
95
-		{ // [11] Test dst + src => expected, error on different key value
96
-			dst:      map[string]string{"foo": "bar"},
97
-			src:      map[string]string{"foo": ""},
98
-			flags:    ErrorOnDifferentDstKeyValue | OverwriteExistingDstKey,
99
-			err:      true,
100
-			expected: map[string]string{"foo": "bar"},
101
-		},
102
-	}
103
-
104
-	for i, test := range testCases {
105
-		err := MergeInto(test.dst, test.src, test.flags)
106
-		if err != nil && !test.err {
107
-			t.Errorf("Unexpected error while merging maps on testCase[%v]: %v.", i, err)
108
-		} else if err == nil && test.err {
109
-			t.Errorf("Unexpected non-error while merging maps on testCase[%v].", i)
110
-		}
111
-
112
-		if !reflect.DeepEqual(test.dst, test.expected) {
113
-			t.Errorf("Unexpected map on testCase[%v]. Expected: %#v, got: %#v.", i, test.expected, test.dst)
114
-		}
115
-	}
116
-}
117 1
new file mode 100644
... ...
@@ -0,0 +1,79 @@
0
+package util
1
+
2
+import (
3
+	"fmt"
4
+
5
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
9
+	buildapi "github.com/openshift/origin/pkg/build/api"
10
+	buildv "github.com/openshift/origin/pkg/build/api/validation"
11
+	deployapi "github.com/openshift/origin/pkg/deploy/api"
12
+	deployv "github.com/openshift/origin/pkg/deploy/api/validation"
13
+	imageapi "github.com/openshift/origin/pkg/image/api"
14
+	imagev "github.com/openshift/origin/pkg/image/api/validation"
15
+	projectapi "github.com/openshift/origin/pkg/project/api"
16
+	projectv "github.com/openshift/origin/pkg/project/api/validation"
17
+	routeapi "github.com/openshift/origin/pkg/route/api"
18
+	routev "github.com/openshift/origin/pkg/route/api/validation"
19
+)
20
+
21
+type fakeServiceLister struct{}
22
+
23
+func (f *fakeServiceLister) ListServices(kapi.Context) (*kapi.ServiceList, error) {
24
+	return &kapi.ServiceList{}, nil
25
+}
26
+
27
+// ValidateObject validates the runtime.Object and returns the validation errors
28
+func ValidateObject(obj runtime.Object) (errors []error) {
29
+	ctx := kapi.NewDefaultContext()
30
+
31
+	if m, err := meta.Accessor(obj); err == nil {
32
+		if len(m.Namespace()) == 0 {
33
+			m.SetNamespace(kapi.NamespaceDefault)
34
+		}
35
+	}
36
+
37
+	switch t := obj.(type) {
38
+	case *kapi.ReplicationController:
39
+		errors = validation.ValidateReplicationController(t)
40
+	case *kapi.Service:
41
+		errors = validation.ValidateService(t, &fakeServiceLister{}, ctx)
42
+	case *kapi.Pod:
43
+		errors = validation.ValidatePod(t)
44
+	case *imageapi.Image:
45
+		errors = imagev.ValidateImage(t)
46
+	case *imageapi.ImageRepository:
47
+		// TODO: validate image repository
48
+		// 	errors = imagev.ValidateImageRepository(t)
49
+	case *imageapi.ImageRepositoryMapping:
50
+		errors = imagev.ValidateImageRepositoryMapping(t)
51
+	case *deployapi.DeploymentConfig:
52
+		errors = deployv.ValidateDeploymentConfig(t)
53
+	case *deployapi.Deployment:
54
+		errors = deployv.ValidateDeployment(t)
55
+	case *projectapi.Project:
56
+		// this is a global resource that should not have a namespace
57
+		t.Namespace = ""
58
+		errors = projectv.ValidateProject(t)
59
+	case *routeapi.Route:
60
+		errors = routev.ValidateRoute(t)
61
+	case *buildapi.BuildConfig:
62
+		errors = buildv.ValidateBuildConfig(t)
63
+	case *buildapi.Build:
64
+		errors = buildv.ValidateBuild(t)
65
+	default:
66
+		if list, err := runtime.ExtractList(obj); err == nil {
67
+			for i := range list {
68
+				errs := ValidateObject(list[i])
69
+				errors = append(errors, errs...)
70
+			}
71
+			return
72
+		}
73
+		// TODO: This should not be an error
74
+		return []error{fmt.Errorf("no validation defined for %#v", obj)}
75
+	}
76
+	return errors
77
+
78
+}