diff --git a/docs/kubectl.md b/docs/kubectl.md index 026e115c81a..a2369001219 100644 --- a/docs/kubectl.md +++ b/docs/kubectl.md @@ -232,7 +232,7 @@ Examples: $ cat pod.json | kubectl update -f - - + $ kubectl update pods my-pod --patch='{ "apiVersion": "v1beta1", "desiredState": { "manifest": [{ "cpu": 100 }]}}' diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index 1038560eed6..18c88f6b6d2 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -67,11 +67,12 @@ Examples: if err := schema.ValidateBytes(data); err != nil { return err } - if err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, data); err != nil { + obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, data) + if err != nil { return err } count++ - // TODO: if generation of names added to server side, change this to use the server's name + info.Refresh(obj, true) fmt.Fprintf(out, "%s\n", info.Name) return nil }) diff --git a/pkg/kubectl/cmd/create_test.go b/pkg/kubectl/cmd/create_test.go index c7a79c5c2b4..0142bedad0e 100644 --- a/pkg/kubectl/cmd/create_test.go +++ b/pkg/kubectl/cmd/create_test.go @@ -26,6 +26,7 @@ import ( func TestCreateObject(t *testing.T) { pods, _ := testData() + pods.Items[0].Name = "redis-master" f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} @@ -81,13 +82,15 @@ func TestCreateMultipleObject(t *testing.T) { cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Run(cmd, []string{}) - if buf.String() != "redis-master\nfrontend\n" { + // Names should come from the REST response, NOT the files + if buf.String() != "foo\nbaz\n" { t.Errorf("unexpected output: %s", buf.String()) } } func TestCreateDirectory(t *testing.T) { pods, svc := testData() + pods.Items[0].Name = "redis-master" f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} @@ -114,7 +117,7 @@ func TestCreateDirectory(t *testing.T) { cmd.Flags().Set("filename", "../../../examples/guestbook") cmd.Run(cmd, []string{}) - if buf.String() != "frontend-controller\nfrontend\nredis-master\nredis-master\nredis-slave-controller\nredisslave\n" { + if buf.String() != "baz\nbaz\nbaz\nredis-master\nbaz\nbaz\n" { t.Errorf("unexpected output: %s", buf.String()) } } diff --git a/pkg/kubectl/cmd/update.go b/pkg/kubectl/cmd/update.go index 69ebb5b19a4..26ec0dfa2e0 100644 --- a/pkg/kubectl/cmd/update.go +++ b/pkg/kubectl/cmd/update.go @@ -42,7 +42,7 @@ Examples: $ cat pod.json | kubectl update -f - - + $ kubectl update pods my-pod --patch='{ "apiVersion": "v1beta1", "desiredState": { "manifest": [{ "cpu": 100 }]}}' `, Run: func(cmd *cobra.Command, args []string) { @@ -67,29 +67,32 @@ Examples: if len(flags.Filenames) != 0 && len(patch) != 0 { usageError(cmd, "Can not specify both --filename and --patch") } - if len(flags.Filenames) > 0 { - err := r.Visit(func(info *resource.Info) error { - data, err := info.Mapping.Codec.Encode(info.Object) - if err != nil { - return err - } - if err := schema.ValidateBytes(data); err != nil { - return err - } - if err := resource.NewHelper(info.Client, info.Mapping). - Update(info.Namespace, info.Name, true, data); err != nil { - return err - } - fmt.Fprintf(out, "%s\n", info.Name) - return nil - }) - checkErr(err) - } else { - // TODO: Make patching work with -f, updating with patched JSON input files + + // TODO: Make patching work with -f, updating with patched JSON input files + if len(flags.Filenames) == 0 { name := updateWithPatch(cmd, args, f, patch) fmt.Fprintf(out, "%s\n", name) + return } + err = r.Visit(func(info *resource.Info) error { + data, err := info.Mapping.Codec.Encode(info.Object) + if err != nil { + return err + } + if err := schema.ValidateBytes(data); err != nil { + return err + } + obj, err := resource.NewHelper(info.Client, info.Mapping).Update(info.Namespace, info.Name, true, data) + if err != nil { + return err + } + info.Refresh(obj, true) + fmt.Fprintf(out, "%s\n", info.Name) + return nil + }) + checkErr(err) + }, } cmd.Flags().VarP(&flags.Filenames, "filename", "f", "Filename, directory, or URL to file to use to update the resource") @@ -115,7 +118,7 @@ func updateWithPatch(cmd *cobra.Command, args []string, f *Factory, patch string data, err := helper.Codec.Encode(obj) checkErr(err) - err = helper.Update(namespace, name, true, data) + obj, err = helper.Update(namespace, name, true, data) checkErr(err) return name } diff --git a/pkg/kubectl/cmd/update_test.go b/pkg/kubectl/cmd/update_test.go index b2ba88a0b33..e17e9458466 100644 --- a/pkg/kubectl/cmd/update_test.go +++ b/pkg/kubectl/cmd/update_test.go @@ -67,7 +67,7 @@ func TestUpdateObject(t *testing.T) { cmd.Run(cmd, []string{}) // uses the name from the file, not the response - if buf.String() != "redis-master\n" { + if buf.String() != "foo\n" { t.Errorf("unexpected output: %s", buf.String()) } } @@ -104,7 +104,7 @@ func TestUpdateMultipleObject(t *testing.T) { cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Run(cmd, []string{}) - if buf.String() != "redis-master\nfrontend\n" { + if buf.String() != "foo\nbaz\n" { t.Errorf("unexpected output: %s", buf.String()) } } @@ -139,7 +139,7 @@ func TestUpdateDirectory(t *testing.T) { cmd.Flags().Set("namespace", "test") cmd.Run(cmd, []string{}) - if buf.String() != "frontend-controller\nfrontend\nredis-master\nredis-master\nredis-slave-controller\nredisslave\n" { + if buf.String() != "qux\nbaz\nbaz\nfoo\nqux\nbaz\n" { t.Errorf("unexpected output: %s", buf.String()) } } diff --git a/pkg/kubectl/resource/helper.go b/pkg/kubectl/resource/helper.go index e12aec49571..0fe6ad5cc40 100644 --- a/pkg/kubectl/resource/helper.go +++ b/pkg/kubectl/resource/helper.go @@ -95,7 +95,7 @@ func (m *Helper) Delete(namespace, name string) error { Error() } -func (m *Helper) Create(namespace string, modify bool, data []byte) error { +func (m *Helper) Create(namespace string, modify bool, data []byte) (runtime.Object, error) { if modify { obj, err := m.Codec.Decode(data) if err != nil { @@ -111,11 +111,11 @@ func (m *Helper) Create(namespace string, modify bool, data []byte) error { } if version != "" { if err := m.Versioner.SetResourceVersion(obj, ""); err != nil { - return err + return nil, err } newData, err := m.Codec.Encode(obj) if err != nil { - return err + return nil, err } data = newData } @@ -124,11 +124,11 @@ func (m *Helper) Create(namespace string, modify bool, data []byte) error { return createResource(m.RESTClient, m.Resource, namespace, data) } -func createResource(c RESTClient, resource, namespace string, data []byte) error { - return c.Post().Namespace(namespace).Resource(resource).Body(data).Do().Error() +func createResource(c RESTClient, resource, namespace string, data []byte) (runtime.Object, error) { + return c.Post().Namespace(namespace).Resource(resource).Body(data).Do().Get() } -func (m *Helper) Update(namespace, name string, overwrite bool, data []byte) error { +func (m *Helper) Update(namespace, name string, overwrite bool, data []byte) (runtime.Object, error) { c := m.RESTClient obj, err := m.Codec.Decode(data) @@ -152,14 +152,14 @@ func (m *Helper) Update(namespace, name string, overwrite bool, data []byte) err } serverVersion, err := m.Versioner.ResourceVersion(serverObj) if err != nil { - return err + return nil, err } if err := m.Versioner.SetResourceVersion(obj, serverVersion); err != nil { - return err + return nil, err } newData, err := m.Codec.Encode(obj) if err != nil { - return err + return nil, err } data = newData } @@ -167,6 +167,6 @@ func (m *Helper) Update(namespace, name string, overwrite bool, data []byte) err return updateResource(c, m.Resource, namespace, name, data) } -func updateResource(c RESTClient, resource, namespace, name string, data []byte) error { - return c.Put().Namespace(namespace).Resource(resource).Name(name).Body(data).Do().Error() +func updateResource(c RESTClient, resource, namespace, name string, data []byte) (runtime.Object, error) { + return c.Put().Namespace(namespace).Resource(resource).Name(name).Body(data).Do().Get() } diff --git a/pkg/kubectl/resource/helper_test.go b/pkg/kubectl/resource/helper_test.go index 11d4e8752d0..f4d752a1da2 100644 --- a/pkg/kubectl/resource/helper_test.go +++ b/pkg/kubectl/resource/helper_test.go @@ -193,7 +193,7 @@ func TestHelperCreate(t *testing.T) { if test.Object != nil { data = []byte(runtime.EncodeOrDie(testapi.Codec(), test.Object)) } - err := modifier.Create("bar", test.Modify, data) + _, err := modifier.Create("bar", test.Modify, data) if (err != nil) != test.Err { t.Errorf("%d: unexpected error: %t %v", i, test.Err, err) } @@ -448,7 +448,7 @@ func TestHelperUpdate(t *testing.T) { if test.Object != nil { data = []byte(runtime.EncodeOrDie(testapi.Codec(), test.Object)) } - err := modifier.Update("bar", "foo", test.Overwrite, data) + _, err := modifier.Update("bar", "foo", test.Overwrite, data) if (err != nil) != test.Err { t.Errorf("%d: unexpected error: %t %v", i, test.Err, err) } diff --git a/pkg/kubectl/resource/visitor.go b/pkg/kubectl/resource/visitor.go index 23f9cf445ec..2c6bcd78bf2 100644 --- a/pkg/kubectl/resource/visitor.go +++ b/pkg/kubectl/resource/visitor.go @@ -87,6 +87,7 @@ func (i *Info) Visit(fn VisitorFunc) error { return fn(i) } +// Get retrieves the object from the Namespace and Name fields func (i *Info) Get() error { obj, err := NewHelper(i.Client, i.Mapping).Get(i.Namespace, i.Name) if err != nil { @@ -97,6 +98,38 @@ func (i *Info) Get() error { return nil } +// Refresh updates the object with another object. If ignoreError is set +// the Object will be updated even if name, namespace, or resourceVersion +// attributes cannot be loaded from the object. +func (i *Info) Refresh(obj runtime.Object, ignoreError bool) error { + name, err := i.Mapping.MetadataAccessor.Name(obj) + if err != nil { + if !ignoreError { + return err + } + } else { + i.Name = name + } + namespace, err := i.Mapping.MetadataAccessor.Namespace(obj) + if err != nil { + if !ignoreError { + return err + } + } else { + i.Namespace = namespace + } + version, err := i.Mapping.MetadataAccessor.ResourceVersion(obj) + if err != nil { + if !ignoreError { + return err + } + } else { + i.ResourceVersion = version + } + i.Object = obj + return nil +} + // Watch returns server changes to this object after it was retrieved. func (i *Info) Watch(resourceVersion string) (watch.Interface, error) { return NewHelper(i.Client, i.Mapping).WatchSingle(i.Namespace, i.Name, resourceVersion)