mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Add support for creating a bulk of resources via kubectl apply
This commit is contained in:
		
							
								
								
									
										92
									
								
								pkg/config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								pkg/config/config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClientFunc returns the RESTClient defined for given resource
 | 
				
			||||||
 | 
					type ClientFunc func(mapping *meta.RESTMapping) (*client.RESTClient, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ApplyItems creates bulk of resources provided by items list. Each item must
 | 
				
			||||||
 | 
					// be valid API type. It requires ObjectTyper to parse the Version and Kind and
 | 
				
			||||||
 | 
					// RESTMapper to get the resource URI and REST client that knows how to create
 | 
				
			||||||
 | 
					// given type
 | 
				
			||||||
 | 
					func CreateObjects(typer runtime.ObjectTyper, mapper meta.RESTMapper, clientFor ClientFunc, objects []runtime.Object) errs.ValidationErrorList {
 | 
				
			||||||
 | 
						allErrors := errs.ValidationErrorList{}
 | 
				
			||||||
 | 
						for i, obj := range objects {
 | 
				
			||||||
 | 
							version, kind, err := typer.ObjectVersionAndKind(obj)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								reportError(&allErrors, i, errs.NewFieldInvalid("kind", obj))
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mapping, err := mapper.RESTMapping(version, kind)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								reportError(&allErrors, i, errs.NewFieldNotSupported("mapping", err))
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							client, err := clientFor(mapping)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								reportError(&allErrors, i, errs.NewFieldNotSupported("client", obj))
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := CreateObject(client, mapping, obj); err != nil {
 | 
				
			||||||
 | 
								reportError(&allErrors, i, *err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return allErrors.Prefix("Config")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Apply creates the obj using the provided clients and the resource URI
 | 
				
			||||||
 | 
					// mapping. It reports ValidationError when the object is missing the Metadata
 | 
				
			||||||
 | 
					// or the Name and it will report any error occured during create REST call
 | 
				
			||||||
 | 
					func CreateObject(client *client.RESTClient, mapping *meta.RESTMapping, obj runtime.Object) *errs.ValidationError {
 | 
				
			||||||
 | 
						name, err := mapping.MetadataAccessor.Name(obj)
 | 
				
			||||||
 | 
						if err != nil || name == "" {
 | 
				
			||||||
 | 
							e := errs.NewFieldRequired("name", err)
 | 
				
			||||||
 | 
							return &e
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						namespace, err := mapping.Namespace(obj)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							e := errs.NewFieldRequired("namespace", err)
 | 
				
			||||||
 | 
							return &e
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: This should be using RESTHelper
 | 
				
			||||||
 | 
						err = client.Post().Path(mapping.Resource).Namespace(namespace).Body(obj).Do().Error()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return &errs.ValidationError{errs.ValidationErrorTypeInvalid, name, err}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// reportError reports the single item validation error and properly set the
 | 
				
			||||||
 | 
					// prefix and index to match the Config item JSON index
 | 
				
			||||||
 | 
					func reportError(allErrs *errs.ValidationErrorList, index int, err errs.ValidationError) {
 | 
				
			||||||
 | 
						i := errs.ValidationErrorList{}
 | 
				
			||||||
 | 
						*allErrs = append(*allErrs, append(i, err).PrefixIndex(index).Prefix("item")...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										164
									
								
								pkg/config/config_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								pkg/config/config_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,164 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/http/httptest"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getTyperAndMapper() (runtime.ObjectTyper, meta.RESTMapper) {
 | 
				
			||||||
 | 
						return api.Scheme, latest.RESTMapper
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getFakeClient(t *testing.T, validURLs []string) (ClientFunc, *httptest.Server) {
 | 
				
			||||||
 | 
						handlerFunc := func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							for _, u := range validURLs {
 | 
				
			||||||
 | 
								if u == r.RequestURI {
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							t.Errorf("Unexpected HTTP request: %s, expected %v", r.RequestURI, validURLs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						server := httptest.NewServer(http.HandlerFunc(handlerFunc))
 | 
				
			||||||
 | 
						return func(mapping *meta.RESTMapping) (*client.RESTClient, error) {
 | 
				
			||||||
 | 
							fakeCodec := runtime.CodecFor(api.Scheme, "v1beta1")
 | 
				
			||||||
 | 
							fakeUri, _ := url.Parse(server.URL + "/api/v1beta1")
 | 
				
			||||||
 | 
							return client.NewRESTClient(fakeUri, fakeCodec), nil
 | 
				
			||||||
 | 
						}, server
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateObjects(t *testing.T) {
 | 
				
			||||||
 | 
						items := []runtime.Object{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						items = append(items, &api.Pod{
 | 
				
			||||||
 | 
							TypeMeta:   api.TypeMeta{APIVersion: "v1beta1", Kind: "Pod"},
 | 
				
			||||||
 | 
							ObjectMeta: api.ObjectMeta{Name: "test-pod"},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						items = append(items, &api.Service{
 | 
				
			||||||
 | 
							TypeMeta:   api.TypeMeta{APIVersion: "v1beta1", Kind: "Service"},
 | 
				
			||||||
 | 
							ObjectMeta: api.ObjectMeta{Name: "test-service"},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						typer, mapper := getTyperAndMapper()
 | 
				
			||||||
 | 
						client, s := getFakeClient(t, []string{"/api/v1beta1/pods", "/api/v1beta1/services"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errs := CreateObjects(typer, mapper, client, items)
 | 
				
			||||||
 | 
						s.Close()
 | 
				
			||||||
 | 
						if len(errs) != 0 {
 | 
				
			||||||
 | 
							t.Errorf("Unexpected errors during config.Create(): %v", errs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateNoNameItem(t *testing.T) {
 | 
				
			||||||
 | 
						items := []runtime.Object{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						items = append(items, &api.Service{
 | 
				
			||||||
 | 
							TypeMeta: api.TypeMeta{APIVersion: "v1beta1", Kind: "Service"},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						typer, mapper := getTyperAndMapper()
 | 
				
			||||||
 | 
						client, s := getFakeClient(t, []string{"/api/v1beta1/services"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errs := CreateObjects(typer, mapper, client, items)
 | 
				
			||||||
 | 
						s.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(errs) == 0 {
 | 
				
			||||||
 | 
							t.Errorf("Expected required value error for missing name")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e := errs[0].(errors.ValidationError)
 | 
				
			||||||
 | 
						if errors.ValueOf(e.Type) != "required value" {
 | 
				
			||||||
 | 
							t.Errorf("Expected ValidationErrorTypeRequired error, got %#v", e)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if e.Field != "Config.item[0].name" {
 | 
				
			||||||
 | 
							t.Errorf("Expected 'Config.item[0].name' as error field, got '%#v'", e.Field)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InvalidItem struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*InvalidItem) IsAnAPIObject() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateInvalidItem(t *testing.T) {
 | 
				
			||||||
 | 
						items := []runtime.Object{
 | 
				
			||||||
 | 
							&InvalidItem{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						typer, mapper := getTyperAndMapper()
 | 
				
			||||||
 | 
						client, s := getFakeClient(t, []string{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errs := CreateObjects(typer, mapper, client, items)
 | 
				
			||||||
 | 
						s.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(errs) == 0 {
 | 
				
			||||||
 | 
							t.Errorf("Expected invalid value error for kind")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e := errs[0].(errors.ValidationError)
 | 
				
			||||||
 | 
						if errors.ValueOf(e.Type) != "invalid value" {
 | 
				
			||||||
 | 
							t.Errorf("Expected ValidationErrorTypeInvalid error, got %#v", e)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if e.Field != "Config.item[0].kind" {
 | 
				
			||||||
 | 
							t.Errorf("Expected 'Config.item[0].kind' as error field, got '%#v'", e.Field)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateNoClientItems(t *testing.T) {
 | 
				
			||||||
 | 
						items := []runtime.Object{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						items = append(items, &api.Pod{
 | 
				
			||||||
 | 
							TypeMeta:   api.TypeMeta{APIVersion: "v1beta1", Kind: "Pod"},
 | 
				
			||||||
 | 
							ObjectMeta: api.ObjectMeta{Name: "test-pod"},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						typer, mapper := getTyperAndMapper()
 | 
				
			||||||
 | 
						_, s := getFakeClient(t, []string{"/api/v1beta1/pods", "/api/v1beta1/services"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						noClientFunc := func(mapping *meta.RESTMapping) (*client.RESTClient, error) {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("no client")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errs := CreateObjects(typer, mapper, noClientFunc, items)
 | 
				
			||||||
 | 
						s.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(errs) == 0 {
 | 
				
			||||||
 | 
							t.Errorf("Expected invalid value error for client")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e := errs[0].(errors.ValidationError)
 | 
				
			||||||
 | 
						if errors.ValueOf(e.Type) != "unsupported value" {
 | 
				
			||||||
 | 
							t.Errorf("Expected ValidationErrorTypeUnsupported error, got %#v", e)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if e.Field != "Config.item[0].client" {
 | 
				
			||||||
 | 
							t.Errorf("Expected 'Config.item[0].client' as error field, got '%#v'", e.Field)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										128
									
								
								pkg/config/config_test.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								pkg/config/config_test.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					[
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "id": "frontend",
 | 
				
			||||||
 | 
					    "name": "frontend",
 | 
				
			||||||
 | 
					    "kind": "Service",
 | 
				
			||||||
 | 
					    "apiVersion": "v1beta2",
 | 
				
			||||||
 | 
					    "port": 5432,
 | 
				
			||||||
 | 
					    "selector": {
 | 
				
			||||||
 | 
					      "name": "frontend"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "id": "redismaster",
 | 
				
			||||||
 | 
					    "name": "redismaster",
 | 
				
			||||||
 | 
					    "kind": "Service",
 | 
				
			||||||
 | 
					    "apiVersion": "v1beta1",
 | 
				
			||||||
 | 
					    "port": 10000,
 | 
				
			||||||
 | 
					    "selector": {
 | 
				
			||||||
 | 
					      "name": "redis-master"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "id": "redisslave",
 | 
				
			||||||
 | 
					    "name": "redisslave",
 | 
				
			||||||
 | 
					    "kind": "Service",
 | 
				
			||||||
 | 
					    "apiVersion": "v1beta1",
 | 
				
			||||||
 | 
					    "port": 10001,
 | 
				
			||||||
 | 
					    "labels": {
 | 
				
			||||||
 | 
					      "name": "redisslave"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "selector": {
 | 
				
			||||||
 | 
					      "name": "redisslave"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "id": "redis-master-2",
 | 
				
			||||||
 | 
					    "name": "redis-master-2",
 | 
				
			||||||
 | 
					    "kind": "Pod",
 | 
				
			||||||
 | 
					    "apiVersion": "v1beta1",
 | 
				
			||||||
 | 
					    "desiredState": {
 | 
				
			||||||
 | 
					      "manifest": {
 | 
				
			||||||
 | 
					        "version": "v1beta1",
 | 
				
			||||||
 | 
					        "containers": [{
 | 
				
			||||||
 | 
					          "name": "master",
 | 
				
			||||||
 | 
					          "image": "dockerfile/redis",
 | 
				
			||||||
 | 
					          "env": [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "name": "REDIS_PASSWORD",
 | 
				
			||||||
 | 
					              "value": "secret"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          "ports": [{
 | 
				
			||||||
 | 
					            "containerPort": 6379
 | 
				
			||||||
 | 
					          }]
 | 
				
			||||||
 | 
					        }]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "labels": {
 | 
				
			||||||
 | 
					      "name": "redis-master"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "id": "frontendController",
 | 
				
			||||||
 | 
					    "name": "frontendController",
 | 
				
			||||||
 | 
					    "kind": "ReplicationController",
 | 
				
			||||||
 | 
					    "apiVersion": "v1beta1",
 | 
				
			||||||
 | 
					    "desiredState": {
 | 
				
			||||||
 | 
					      "replicas": 3,
 | 
				
			||||||
 | 
					      "replicaSelector": {"name": "frontend"},
 | 
				
			||||||
 | 
					      "podTemplate": {
 | 
				
			||||||
 | 
					        "desiredState": {
 | 
				
			||||||
 | 
					          "manifest": {
 | 
				
			||||||
 | 
					            "version": "v1beta1",
 | 
				
			||||||
 | 
					            "containers": [{
 | 
				
			||||||
 | 
					              "name": "php-redis",
 | 
				
			||||||
 | 
					              "image": "brendanburns/php-redis",
 | 
				
			||||||
 | 
					              "env": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "name": "ADMIN_USERNAME",
 | 
				
			||||||
 | 
					                  "value": "admin"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "name": "ADMIN_PASSWORD",
 | 
				
			||||||
 | 
					                  "value": "secret"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "name": "REDIS_PASSWORD",
 | 
				
			||||||
 | 
					                  "value": "secret"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					              "ports": [{"containerPort": 80}]
 | 
				
			||||||
 | 
					            }]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "labels": {"name": "frontend"}
 | 
				
			||||||
 | 
					      }},
 | 
				
			||||||
 | 
					      "labels": {"name": "frontend"}
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "id": "redisSlaveController",
 | 
				
			||||||
 | 
					    "name": "redisSlaveController",
 | 
				
			||||||
 | 
					    "kind": "ReplicationController",
 | 
				
			||||||
 | 
					    "apiVersion": "v1beta1",
 | 
				
			||||||
 | 
					    "desiredState": {
 | 
				
			||||||
 | 
					      "replicas": 2,
 | 
				
			||||||
 | 
					      "replicaSelector": {"name": "redisslave"},
 | 
				
			||||||
 | 
					      "podTemplate": {
 | 
				
			||||||
 | 
					        "desiredState": {
 | 
				
			||||||
 | 
					          "manifest": {
 | 
				
			||||||
 | 
					            "version": "v1beta1",
 | 
				
			||||||
 | 
					            "containers": [{
 | 
				
			||||||
 | 
					              "name": "slave",
 | 
				
			||||||
 | 
					              "image": "brendanburns/redis-slave",
 | 
				
			||||||
 | 
					              "env": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  "name": "REDIS_PASSWORD",
 | 
				
			||||||
 | 
					                  "value": "secret"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					              "ports": [{"containerPort": 6379}]
 | 
				
			||||||
 | 
					            }]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "labels": {"name": "redisslave"}
 | 
				
			||||||
 | 
					      }},
 | 
				
			||||||
 | 
					      "labels": {"name": "redisslave"}
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
@@ -68,6 +68,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
 | 
				
			|||||||
	cmds.AddCommand(NewCmdDelete(out))
 | 
						cmds.AddCommand(NewCmdDelete(out))
 | 
				
			||||||
	cmds.AddCommand(NewCmdNamespace(out))
 | 
						cmds.AddCommand(NewCmdNamespace(out))
 | 
				
			||||||
	cmds.AddCommand(NewCmdLog(out))
 | 
						cmds.AddCommand(NewCmdLog(out))
 | 
				
			||||||
 | 
						cmds.AddCommand(NewCmdCreateAll(out))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := cmds.Execute(); err != nil {
 | 
						if err := cmds.Execute(); err != nil {
 | 
				
			||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										116
									
								
								pkg/kubectl/cmd/createall.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								pkg/kubectl/cmd/createall.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package cmd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
				
			||||||
 | 
						errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/config"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
				
			||||||
 | 
						"github.com/golang/glog"
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
						"gopkg.in/v1/yaml"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DataToObjects converts the raw JSON data into API objects
 | 
				
			||||||
 | 
					func DataToObjects(m meta.RESTMapper, t runtime.ObjectTyper, data []byte) (result []runtime.Object, errors errs.ValidationErrorList) {
 | 
				
			||||||
 | 
						configObj := []runtime.RawExtension{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := yaml.Unmarshal(data, &configObj); err != nil {
 | 
				
			||||||
 | 
							errors = append(errors, errs.NewFieldInvalid("unmarshal", err))
 | 
				
			||||||
 | 
							return result, errors.Prefix("Config")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, in := range configObj {
 | 
				
			||||||
 | 
							version, kind, err := t.DataVersionAndKind(in.RawJSON)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								itemErrs := errs.ValidationErrorList{}
 | 
				
			||||||
 | 
								itemErrs = append(itemErrs, errs.NewFieldInvalid("kind", string(in.RawJSON)))
 | 
				
			||||||
 | 
								errors = append(errors, itemErrs.PrefixIndex(i).Prefix("item")...)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mapping, err := m.RESTMapping(version, kind)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								itemErrs := errs.ValidationErrorList{}
 | 
				
			||||||
 | 
								itemErrs = append(itemErrs, errs.NewFieldRequired("mapping", err))
 | 
				
			||||||
 | 
								errors = append(errors, itemErrs.PrefixIndex(i).Prefix("item")...)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							obj, err := mapping.Codec.Decode(in.RawJSON)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								itemErrs := errs.ValidationErrorList{}
 | 
				
			||||||
 | 
								itemErrs = append(itemErrs, errs.NewFieldInvalid("decode", err))
 | 
				
			||||||
 | 
								errors = append(errors, itemErrs.PrefixIndex(i).Prefix("item")...)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							result = append(result, obj)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewCmdCreateAll(out io.Writer) *cobra.Command {
 | 
				
			||||||
 | 
						cmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "createall -f filename",
 | 
				
			||||||
 | 
							Short: "Create all resources specified in filename or stdin",
 | 
				
			||||||
 | 
							Long: `Create all resources contained in JSON file specified in filename or stdin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					JSON and YAML formats are accepted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Examples:
 | 
				
			||||||
 | 
					  $ kubectl createall -f config.json
 | 
				
			||||||
 | 
					  <creates all resources listed in config.json>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $ cat config.json | kubectl apply -f -
 | 
				
			||||||
 | 
					  <creates all resources listed in config.json>`,
 | 
				
			||||||
 | 
							Run: func(cmd *cobra.Command, args []string) {
 | 
				
			||||||
 | 
								// TODO: Replace this with Factory.Typer
 | 
				
			||||||
 | 
								typer := api.Scheme
 | 
				
			||||||
 | 
								// TODO: Replace this with Factory.Mapper
 | 
				
			||||||
 | 
								mapper := latest.RESTMapper
 | 
				
			||||||
 | 
								// TODO: Replace this with Factory.Client
 | 
				
			||||||
 | 
								clientFunc := func(*meta.RESTMapping) (*client.RESTClient, error) {
 | 
				
			||||||
 | 
									return getKubeClient(cmd).RESTClient, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								filename := getFlagString(cmd, "filename")
 | 
				
			||||||
 | 
								if len(filename) == 0 {
 | 
				
			||||||
 | 
									usageError(cmd, "Must pass a filename to update")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								data, err := readConfigData(filename)
 | 
				
			||||||
 | 
								checkErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								items, errs := DataToObjects(mapper, typer, data)
 | 
				
			||||||
 | 
								applyErrs := config.CreateObjects(typer, mapper, clientFunc, items)
 | 
				
			||||||
 | 
								errs = append(errs, applyErrs...)
 | 
				
			||||||
 | 
								if len(errs) > 0 {
 | 
				
			||||||
 | 
									for _, e := range errs {
 | 
				
			||||||
 | 
										glog.Error(e)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cmd.Flags().StringP("filename", "f", "", "Filename or URL to file to use to update the resource")
 | 
				
			||||||
 | 
						return cmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user