Unify command line namespace resolution

This change allows the namespace in kubeconfig to be overridden by
specifying the namespace in the spec file. If namespace is explicitly
provided in the command line flags and the spec file has a different
namespace, this will cause an error.
This commit is contained in:
Kris Rousey
2015-06-26 13:49:34 -07:00
parent 588bc9beb5
commit ffa764d60b
23 changed files with 120 additions and 57 deletions

View File

@@ -57,7 +57,7 @@ func RunClusterInfo(factory *cmdutil.Factory, out io.Writer, cmd *cobra.Command)
printService(out, "Kubernetes master", client.Host)
mapper, typer := factory.Object()
cmdNamespace, err := factory.DefaultNamespace()
cmdNamespace, _, err := factory.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -146,8 +146,8 @@ func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
Validator: func() (validation.Schema, error) {
return t.Validator, t.Err
},
DefaultNamespace: func() (string, error) {
return t.Namespace, t.Err
DefaultNamespace: func() (string, bool, error) {
return t.Namespace, false, t.Err
},
ClientConfig: func() (*client.Config, error) {
return t.ClientConfig, t.Err
@@ -200,8 +200,8 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
Validator: func() (validation.Schema, error) {
return t.Validator, t.Err
},
DefaultNamespace: func() (string, error) {
return t.Namespace, t.Err
DefaultNamespace: func() (string, bool, error) {
return t.Namespace, false, t.Err
},
ClientConfig: func() (*client.Config, error) {
return t.ClientConfig, t.Err

View File

@@ -75,7 +75,7 @@ func RunCreate(f *cmdutil.Factory, out io.Writer, filenames util.StringList) err
return err
}
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
@@ -84,8 +84,8 @@ func RunCreate(f *cmdutil.Factory, out io.Writer, filenames util.StringList) err
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
Schema(schema).
ContinueOnError().
NamespaceParam(cmdNamespace).RequireNamespace().
FilenameParam(filenames...).
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, filenames...).
Flatten().
Do()
err = r.Err()

View File

@@ -82,7 +82,7 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command {
}
func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error {
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
@@ -90,7 +90,7 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(filenames...).
FilenameParam(enforceNamespace, filenames...).
SelectorParam(cmdutil.GetFlagString(cmd, "selector")).
SelectAllParam(cmdutil.GetFlagBool(cmd, "all")).
ResourceTypeOrNameArgs(false, args...).RequireObject(false).

View File

@@ -59,7 +59,7 @@ $ kubectl describe po -l name=myLabel`,
func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
selector := cmdutil.GetFlagString(cmd, "selector")
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -104,7 +104,7 @@ func extractPodAndContainer(cmd *cobra.Command, argsIn []string, p *execParams)
func RunExec(f *cmdutil.Factory, cmd *cobra.Command, cmdIn io.Reader, cmdOut, cmdErr io.Writer, p *execParams, argsIn []string, re remoteExecutor) error {
podName, containerName, args, err := extractPodAndContainer(cmd, argsIn, p)
namespace, err := f.DefaultNamespace()
namespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -74,7 +74,7 @@ func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command {
}
func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
namespace, err := f.DefaultNamespace()
namespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -92,7 +92,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
allNamespaces := cmdutil.GetFlagBool(cmd, "all-namespaces")
mapper, typer := f.Object()
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -180,7 +180,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
overwrite := cmdutil.GetFlagBool(cmd, "overwrite")
resourceVersion := cmdutil.GetFlagString(cmd, "resource-version")
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -94,7 +94,7 @@ func RunLog(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
return cmdutil.UsageError(cmd, "log POD [CONTAINER]")
}
namespace, err := f.DefaultNamespace()
namespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -53,7 +53,7 @@ func NewCmdPatch(f *cmdutil.Factory, out io.Writer) *cobra.Command {
}
func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -83,7 +83,7 @@ func RunPortForward(f *cmdutil.Factory, cmd *cobra.Command, args []string, fw po
return cmdutil.UsageError(cmd, "at least 1 PORT is required for port-forward")
}
namespace, err := f.DefaultNamespace()
namespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -77,7 +77,7 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st
return err
}
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
@@ -95,8 +95,8 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
Schema(schema).
ContinueOnError().
NamespaceParam(cmdNamespace).RequireNamespace().
FilenameParam(filenames...).
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, filenames...).
Flatten().
Do()
err = r.Err()
@@ -126,7 +126,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
return err
}
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
@@ -135,7 +135,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(filenames...).
FilenameParam(enforceNamespace, filenames...).
ResourceTypeOrNameArgs(false, args...).RequireObject(false).
Flatten().
Do()
@@ -159,8 +159,8 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
r = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
Schema(schema).
ContinueOnError().
NamespaceParam(cmdNamespace).RequireNamespace().
FilenameParam(filenames...).
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, filenames...).
Flatten().
Do()
err = r.Err()

View File

@@ -117,7 +117,7 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
timeout := cmdutil.GetFlagDuration(cmd, "timeout")
dryrun := cmdutil.GetFlagBool(cmd, "dry-run")
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
@@ -154,10 +154,11 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
if err != nil {
return err
}
request := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
Schema(schema).
NamespaceParam(cmdNamespace).RequireNamespace().
FilenameParam(filename).
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, filename).
Do()
obj, err := request.Object()
if err != nil {

View File

@@ -78,7 +78,7 @@ func Run(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) e
return cmdutil.UsageError(cmd, "NAME is required for run")
}
namespace, err := f.DefaultNamespace()
namespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -74,7 +74,7 @@ func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
return cmdutil.UsageError(cmd, "--replicas=COUNT RESOURCE ID")
}
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}

View File

@@ -68,16 +68,16 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command {
}
func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, filenames util.StringList, out io.Writer) error {
cmdNamespace, err := f.DefaultNamespace()
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
ContinueOnError().
NamespaceParam(cmdNamespace).RequireNamespace().
NamespaceParam(cmdNamespace).DefaultNamespace().
ResourceTypeOrNameArgs(false, args...).
FilenameParam(filenames...).
FilenameParam(enforceNamespace, filenames...).
SelectorParam(cmdutil.GetFlagString(cmd, "selector")).
SelectAllParam(cmdutil.GetFlagBool(cmd, "all")).
Flatten().

View File

@@ -78,8 +78,10 @@ type Factory struct {
LabelsForObject func(object runtime.Object) (map[string]string, error)
// Returns a schema that can validate objects stored on disk.
Validator func() (validation.Schema, error)
// Returns the default namespace to use in cases where no other namespace is specified
DefaultNamespace func() (string, error)
// Returns the default namespace to use in cases where no
// other namespace is specified and whether the namespace was
// overriden.
DefaultNamespace func() (string, bool, error)
// Returns the generator for the provided generator name
Generator func(name string) (kubectl.Generator, bool)
}
@@ -209,7 +211,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
}
return validation.NullSchema{}, nil
},
DefaultNamespace: func() (string, error) {
DefaultNamespace: func() (string, bool, error) {
return clientConfig.Namespace()
},
Generator: func(name string) (kubectl.Generator, bool) {

View File

@@ -87,9 +87,12 @@ func (b *Builder) Schema(schema validation.Schema) *Builder {
}
// FilenameParam groups input in two categories: URLs and files (files, directories, STDIN)
// If enforceNamespace is false, namespaces in the specs will be allowed to
// override the default namespace. If it is true, namespaces that don't match
// will cause an error.
// If ContinueOnError() is set prior to this method, objects on the path that are not
// recognized will be ignored (but logged at V(2)).
func (b *Builder) FilenameParam(paths ...string) *Builder {
func (b *Builder) FilenameParam(enforceNamespace bool, paths ...string) *Builder {
for _, s := range paths {
switch {
case s == "-":
@@ -105,6 +108,11 @@ func (b *Builder) FilenameParam(paths ...string) *Builder {
b.Path(s)
}
}
if enforceNamespace {
b.RequireNamespace()
}
return b
}

View File

@@ -177,7 +177,7 @@ func (v *testVisitor) Objects() []runtime.Object {
func TestPathBuilder(t *testing.T) {
b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
FilenameParam("../../../examples/guestbook/redis-master-controller.yaml")
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml")
test := &testVisitor{}
singular := false
@@ -227,8 +227,8 @@ func TestNodeBuilder(t *testing.T) {
func TestPathBuilderWithMultiple(t *testing.T) {
b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
FilenameParam("../../../examples/guestbook/redis-master-controller.yaml").
FilenameParam("../../../examples/guestbook/redis-master-controller.yaml").
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").
NamespaceParam("test").DefaultNamespace()
test := &testVisitor{}
@@ -247,7 +247,7 @@ func TestPathBuilderWithMultiple(t *testing.T) {
func TestDirectoryBuilder(t *testing.T) {
b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
FilenameParam("../../../examples/guestbook").
FilenameParam(false, "../../../examples/guestbook").
NamespaceParam("test").DefaultNamespace()
test := &testVisitor{}
@@ -269,6 +269,36 @@ func TestDirectoryBuilder(t *testing.T) {
}
}
func TestNamespaceOverride(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(runtime.EncodeOrDie(latest.Codec, &api.Pod{ObjectMeta: api.ObjectMeta{Namespace: "foo", Name: "test"}})))
}))
defer s.Close()
b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
FilenameParam(false, s.URL).
NamespaceParam("test")
test := &testVisitor{}
err := b.Do().Visit(test.Handle)
if err != nil || len(test.Infos) != 1 && test.Infos[0].Namespace != "foo" {
t.Fatalf("unexpected response: %v %#v", err, test.Infos)
}
b = NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
FilenameParam(true, s.URL).
NamespaceParam("test")
test = &testVisitor{}
err = b.Do().Visit(test.Handle)
if err == nil {
t.Fatalf("expected namespace error. got: %#v", test.Infos)
}
}
func TestURLBuilder(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
@@ -277,7 +307,7 @@ func TestURLBuilder(t *testing.T) {
defer s.Close()
b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
FilenameParam(s.URL).
FilenameParam(false, s.URL).
NamespaceParam("test")
test := &testVisitor{}
@@ -301,7 +331,7 @@ func TestURLBuilderRequireNamespace(t *testing.T) {
defer s.Close()
b := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
FilenameParam(s.URL).
FilenameParam(false, s.URL).
NamespaceParam("test").RequireNamespace()
test := &testVisitor{}
@@ -640,7 +670,7 @@ func TestContinueOnErrorVisitor(t *testing.T) {
func TestSingularObject(t *testing.T) {
obj, err := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
NamespaceParam("test").DefaultNamespace().
FilenameParam("../../../examples/guestbook/redis-master-controller.yaml").
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").
Flatten().
Do().Object()
@@ -751,7 +781,7 @@ func TestWatch(t *testing.T) {
}),
})).
NamespaceParam("test").DefaultNamespace().
FilenameParam("../../../examples/guestbook/redis-master-service.yaml").Flatten().
FilenameParam(false, "../../../examples/guestbook/redis-master-service.yaml").Flatten().
Do().Watch("12")
if err != nil {
@@ -778,8 +808,8 @@ func TestWatch(t *testing.T) {
func TestWatchMultipleError(t *testing.T) {
_, err := NewBuilder(latest.RESTMapper, api.Scheme, fakeClient()).
NamespaceParam("test").DefaultNamespace().
FilenameParam("../../../examples/guestbook/redis-master-controller.yaml").Flatten().
FilenameParam("../../../examples/guestbook/redis-master-controller.yaml").Flatten().
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten().
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten().
Do().Watch("")
if err == nil {