diff --git a/hack/common.sh b/hack/common.sh index f500a6910647..2ea04915617a 100755 --- a/hack/common.sh +++ b/hack/common.sh @@ -54,6 +54,7 @@ readonly OPENSHIFT_BINARY_SYMLINKS=( openshift-sti-build openshift-docker-build osc + osadm ) readonly OPENSHIFT_BINARY_COPY=( osc diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 06ce85666881..c168323eaa81 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -170,27 +170,34 @@ echo "templates: ok" # verify some default commands [ "$(openshift cli)" ] [ "$(openshift ex)" ] -[ "$(openshift ex config 2>&1)" ] +[ "$(openshift admin config 2>&1)" ] +[ "$(openshift cli config 2>&1)" ] [ "$(openshift ex tokens)" ] -[ "$(openshift ex policy 2>&1)" ] +[ "$(openshift admin policy 2>&1)" ] [ "$(openshift kubectl 2>&1)" ] [ "$(openshift kube 2>&1)" ] +[ "$(openshift admin 2>&1)" ] # help for root commands must be consistent -[ "$(openshift | grep 'OpenShift for Admins')" ] +[ "$(openshift | grep 'OpenShift Application Platform')" ] [ "$(osc | grep 'OpenShift Client')" ] [ "$(openshift cli | grep 'OpenShift Client')" ] [ "$(openshift kubectl 2>&1 | grep 'Kubernetes cluster')" ] +[ "$(osadm 2>&1 | grep 'OpenShift Administrative Commands')" ] +[ "$(openshift admin 2>&1 | grep 'OpenShift Administrative Commands')" ] # help for root commands with --help flag must be consistent -[ "$(openshift --help 2>&1 | grep 'OpenShift for Admins')" ] +[ "$(openshift --help 2>&1 | grep 'OpenShift Application Platform')" ] [ "$(osc --help 2>&1 | grep 'OpenShift Client')" ] [ "$(openshift cli --help 2>&1 | grep 'OpenShift Client')" ] [ "$(openshift kubectl --help 2>&1 | grep 'Kubernetes cluster')" ] +[ "$(osadm --help 2>&1 | grep 'OpenShift Administrative Commands')" ] +[ "$(openshift admin --help 2>&1 | grep 'OpenShift Administrative Commands')" ] # help for root commands through help command must be consistent [ "$(openshift help cli 2>&1 | grep 'OpenShift Client')" ] [ "$(openshift help kubectl 2>&1 | grep 'Kubernetes cluster')" ] +[ "$(openshift help admin 2>&1 | grep 'OpenShift Administrative Commands')" ] # help for given command with --help flag must be consistent [ "$(osc get --help 2>&1 | grep 'Display one or many resources')" ] @@ -308,44 +315,44 @@ osc describe build ${started} | grep openshift/ruby-20-centos7:success$ osc cancel-build "${started}" --dump-logs --restart echo "cancel-build: ok" -openshift ex policy add-group cluster-admin system:unauthenticated -openshift ex policy remove-group cluster-admin system:unauthenticated -openshift ex policy remove-group-from-project system:unauthenticated -openshift ex policy add-user cluster-admin system:no-user -openshift ex policy remove-user cluster-admin system:no-user -openshift ex policy remove-user-from-project system:no-user +openshift admin policy add-role-to-group cluster-admin system:unauthenticated +openshift admin policy remove-role-from-group cluster-admin system:unauthenticated +openshift admin policy remove-role-from-group-from-project system:unauthenticated +openshift admin policy add-role-to-user cluster-admin system:no-user +openshift admin policy remove-user cluster-admin system:no-user +openshift admin policy remove-user-from-project system:no-user echo "ex policy: ok" # Test the commands the UI projects page tells users to run # These should match what is described in projects.html -openshift ex new-project ui-test-project --admin="anypassword:createuser" -openshift ex policy add-user admin anypassword:adduser -n ui-test-project +osadm new-project ui-test-project --admin="anypassword:createuser" +osadm policy add-role-to-user admin anypassword:adduser -n ui-test-project # Make sure project can be listed by osc (after auth cache syncs) -sleep 2 && osc get projects | grep 'ui-test-project' +sleep 2 && [ "$(osc get projects | grep 'ui-test-project')" ] # Make sure users got added -osc describe policybinding master -n ui-test-project | grep createuser -osc describe policybinding master -n ui-test-project | grep adduser +[ "$(osc describe policybinding master -n ui-test-project | grep createuser)" ] +[ "$(osc describe policybinding master -n ui-test-project | grep adduser)" ] echo "ui-project-commands: ok" # Test deleting and recreating a project -openshift ex new-project recreated-project --admin="anypassword:createuser1" +osadm new-project recreated-project --admin="anypassword:createuser1" osc delete project recreated-project -openshift ex new-project recreated-project --admin="anypassword:createuser2" +osadm new-project recreated-project --admin="anypassword:createuser2" osc describe policybinding master -n recreated-project | grep anypassword:createuser2 echo "ex new-project: ok" # Test running a router -[ ! "$(openshift ex router | grep 'does not exist')"] -[ "$(openshift ex router -o yaml --credentials="${OPENSHIFTCONFIG}" | grep 'openshift/origin-haproxy-')" ] -openshift ex router --create --credentials="${OPENSHIFTCONFIG}" -[ "$(openshift ex router | grep 'service exists')" ] +[ ! "$(osadm router | grep 'does not exist')" ] +[ "$(osadm router -o yaml --credentials="${OPENSHIFTCONFIG}" | grep 'openshift/origin-haproxy-')" ] +osadm router --create --credentials="${OPENSHIFTCONFIG}" +[ "$(osadm router | grep 'service exists')" ] echo "ex router: ok" # Test running a registry -[ ! "$(openshift ex registry | grep 'does not exist')"] -[ "$(openshift ex registry -o yaml --credentials="${OPENSHIFTCONFIG}" | grep 'openshift/origin-docker-registry')" ] -openshift ex registry --create --credentials="${OPENSHIFTCONFIG}" -[ "$(openshift ex registry | grep 'service exists')" ] +[ ! "$(osadm registry | grep 'does not exist')"] +[ "$(osadm registry -o yaml --credentials="${OPENSHIFTCONFIG}" | grep 'openshift/origin-docker-registry')" ] +osadm registry --create --credentials="${OPENSHIFTCONFIG}" +[ "$(osadm registry | grep 'service exists')" ] echo "ex registry: ok" # verify the image repository had its tags populated diff --git a/pkg/cmd/admin/admin.go b/pkg/cmd/admin/admin.go new file mode 100644 index 000000000000..73a35eb35438 --- /dev/null +++ b/pkg/cmd/admin/admin.go @@ -0,0 +1,74 @@ +package admin + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/openshift/origin/pkg/cmd/experimental/buildchain" + "github.com/openshift/origin/pkg/cmd/experimental/config" + "github.com/openshift/origin/pkg/cmd/experimental/policy" + "github.com/openshift/origin/pkg/cmd/experimental/project" + exregistry "github.com/openshift/origin/pkg/cmd/experimental/registry" + exrouter "github.com/openshift/origin/pkg/cmd/experimental/router" + "github.com/openshift/origin/pkg/cmd/server/admin" + "github.com/openshift/origin/pkg/cmd/templates" + "github.com/openshift/origin/pkg/cmd/util/clientcmd" + "github.com/openshift/origin/pkg/version" +) + +const longDesc = ` +OpenShift Administrative Commands + +Commands for managing an OpenShift cluster are exposed here. Many administrative +actions involve interaction with the OpenShift client as well. + +Note: This is a beta release of OpenShift and may change significantly. See + https://github.com/openshift/origin for the latest information on OpenShift. +` + +func NewCommandAdmin(name, fullName string) *cobra.Command { + // Main command + cmd := &cobra.Command{ + Use: name, + Short: "tools for managing an OpenShift cluster", + Long: fmt.Sprintf(longDesc), + Run: func(c *cobra.Command, args []string) { + c.SetOutput(os.Stdout) + c.Help() + }, + } + + f := clientcmd.New(cmd.PersistentFlags()) + //in := os.Stdin + out := os.Stdout + + templates.UseAdminTemplates(cmd) + + cmd.AddCommand(project.NewCmdNewProject(f, fullName, "new-project")) + cmd.AddCommand(policy.NewCommandPolicy(f, fullName, "policy")) + cmd.AddCommand(exrouter.NewCmdRouter(f, fullName, "router", out)) + cmd.AddCommand(exregistry.NewCmdRegistry(f, fullName, "registry", out)) + cmd.AddCommand(buildchain.NewCmdBuildChain(f, fullName, "build-chain")) + cmd.AddCommand(config.NewCmdConfig(fullName, "config")) + + // TODO: these probably belong in a sub command + cmd.AddCommand(admin.NewCommandCreateKubeConfig()) + cmd.AddCommand(admin.NewCommandCreateBootstrapPolicyFile()) + cmd.AddCommand(admin.NewCommandOverwriteBootstrapPolicy(out)) + cmd.AddCommand(admin.NewCommandNodeConfig()) + // TODO: these should be rolled up together + cmd.AddCommand(admin.NewCommandCreateAllCerts()) + cmd.AddCommand(admin.NewCommandCreateClientCert()) + cmd.AddCommand(admin.NewCommandCreateNodeClientCert()) + cmd.AddCommand(admin.NewCommandCreateServerCert()) + cmd.AddCommand(admin.NewCommandCreateSignerCert()) + cmd.AddCommand(admin.NewCommandCreateClient()) + + if name == fullName { + cmd.AddCommand(version.NewVersionCommand(fullName)) + } + + return cmd +} diff --git a/pkg/cmd/cli/cli.go b/pkg/cmd/cli/cli.go index fad24ffd55d3..c3eccd7a7157 100644 --- a/pkg/cmd/cli/cli.go +++ b/pkg/cmd/cli/cli.go @@ -9,6 +9,7 @@ import ( "github.com/spf13/pflag" "github.com/openshift/origin/pkg/cmd/cli/cmd" + "github.com/openshift/origin/pkg/cmd/experimental/config" "github.com/openshift/origin/pkg/cmd/templates" "github.com/openshift/origin/pkg/cmd/util/clientcmd" "github.com/openshift/origin/pkg/version" @@ -64,6 +65,7 @@ func NewCommandCLI(name, fullName string) *cobra.Command { templates.UseCliTemplates(cmds) cmds.AddCommand(cmd.NewCmdLogin(f, in, out)) + cmds.AddCommand(cmd.NewCmdProject(f, out)) cmds.AddCommand(cmd.NewCmdNewApplication(fullName, f, out)) cmds.AddCommand(cmd.NewCmdStartBuild(fullName, f, out)) cmds.AddCommand(cmd.NewCmdCancelBuild(fullName, f, out)) @@ -80,9 +82,11 @@ func NewCommandCLI(name, fullName string) *cobra.Command { cmds.AddCommand(cmd.NewCmdExec(fullName, f, os.Stdin, out, os.Stderr)) cmds.AddCommand(cmd.NewCmdPortForward(fullName, f)) cmds.AddCommand(f.NewCmdProxy(out)) - cmds.AddCommand(cmd.NewCmdProject(f, out)) cmds.AddCommand(cmd.NewCmdOptions(f, out)) - cmds.AddCommand(version.NewVersionCommand(fullName)) + if name == fullName { + cmds.AddCommand(version.NewVersionCommand(fullName)) + } + cmds.AddCommand(config.NewCmdConfig(fullName, "config")) return cmds } diff --git a/pkg/cmd/cli/describe/describer.go b/pkg/cmd/cli/describe/describer.go index 1d3b49a8b950..71654fdc1af0 100644 --- a/pkg/cmd/cli/describe/describer.go +++ b/pkg/cmd/cli/describe/describer.go @@ -480,8 +480,12 @@ func (d *RoleBindingDescriber) Describe(namespace, name string) (string, error) return "", err } - role, roleErr := d.Roles(roleBinding.RoleRef.Namespace).Get(roleBinding.RoleRef.Name) + role, err := d.Roles(roleBinding.RoleRef.Namespace).Get(roleBinding.RoleRef.Name) + return DescribeRoleBinding(roleBinding, role, err) +} +// DescribeRoleBinding prints out information about a role binding and its associated role +func DescribeRoleBinding(roleBinding *authorizationapi.RoleBinding, role *authorizationapi.Role, err error) (string, error) { return tabbedString(func(out *tabwriter.Writer) error { formatMeta(out, roleBinding.ObjectMeta) @@ -489,14 +493,32 @@ func (d *RoleBindingDescriber) Describe(namespace, name string) (string, error) formatString(out, "Users", roleBinding.Users.List()) formatString(out, "Groups", roleBinding.Groups.List()) - if roleErr != nil { - formatString(out, "ROLE RESOLUTION ERROR", roleErr) + switch { + case err != nil: + formatString(out, "Policy Rules", fmt.Sprintf("error: %v", err)) - } else { + case role != nil: fmt.Fprint(out, policyRuleHeadings+"\n") for _, rule := range role.Rules { describePolicyRule(out, rule, "") } + + default: + formatString(out, "Policy Rules", "") + } + + return nil + }) +} + +// DescribeRole prints out information about a role +func DescribeRole(role *authorizationapi.Role) (string, error) { + return tabbedString(func(out *tabwriter.Writer) error { + formatMeta(out, role.ObjectMeta) + + fmt.Fprint(out, policyRuleHeadings+"\n") + for _, rule := range role.Rules { + describePolicyRule(out, rule, "") } return nil diff --git a/pkg/cmd/experimental/config/config.go b/pkg/cmd/experimental/config/config.go index 08afe17eba27..24f2783caaa6 100644 --- a/pkg/cmd/experimental/config/config.go +++ b/pkg/cmd/experimental/config/config.go @@ -10,7 +10,8 @@ import ( func NewCmdConfig(parentName, name string) *cobra.Command { cmd := config.NewCmdConfig(os.Stdout) - cmd.Long = fmt.Sprintf(`Manages .kubeconfig files using subcommands like: + cmd.Short = "Change configuration files for the client" + cmd.Long = fmt.Sprintf(`Manages the OpenShift config files using subcommands like: %[1]s %[2]s use-context my-context %[1]s %[2]s set preferences.some true diff --git a/pkg/cmd/experimental/policy/add_group.go b/pkg/cmd/experimental/policy/add_group.go index d2874caef850..12de82fbe474 100644 --- a/pkg/cmd/experimental/policy/add_group.go +++ b/pkg/cmd/experimental/policy/add_group.go @@ -25,9 +25,9 @@ func NewCmdAddGroup(f *clientcmd.Factory) *cobra.Command { options := &addGroupOptions{} cmd := &cobra.Command{ - Use: "add-group [group]...", - Short: "add group to role", - Long: `add group to role`, + Use: "add-role-to-group", + Short: "add groups to a role", + Long: `add groups to a role`, Run: func(cmd *cobra.Command, args []string) { if !options.complete(cmd) { return diff --git a/pkg/cmd/experimental/policy/add_user.go b/pkg/cmd/experimental/policy/add_user.go index 0c0646221510..c66c607944fb 100644 --- a/pkg/cmd/experimental/policy/add_user.go +++ b/pkg/cmd/experimental/policy/add_user.go @@ -25,12 +25,12 @@ func NewCmdAddUser(f *clientcmd.Factory) *cobra.Command { options := &AddUserOptions{} cmd := &cobra.Command{ - Use: "add-user [user]...", - Short: "add user to role", - Long: `add user to role`, + Use: "add-role-to-user", + Short: "add users to a role", + Long: `add users to a role`, Run: func(cmd *cobra.Command, args []string) { - if !options.complete(cmd) { - return + if !options.complete(cmd, args) { + glog.Fatalf("You must specify two arguments") } var err error @@ -51,10 +51,8 @@ func NewCmdAddUser(f *clientcmd.Factory) *cobra.Command { return cmd } -func (o *AddUserOptions) complete(cmd *cobra.Command) bool { - args := cmd.Flags().Args() +func (o *AddUserOptions) complete(cmd *cobra.Command, args []string) bool { if len(args) < 2 { - cmd.Help() return false } diff --git a/pkg/cmd/experimental/policy/policy.go b/pkg/cmd/experimental/policy/policy.go index 213854df3bdc..9df326ebd75f 100644 --- a/pkg/cmd/experimental/policy/policy.go +++ b/pkg/cmd/experimental/policy/policy.go @@ -20,8 +20,8 @@ func NewCommandPolicy(f *clientcmd.Factory, parentName, name string) *cobra.Comm // Parent command to which all subcommands are added. cmds := &cobra.Command{ Use: name, - Short: "manage authorization policy", - Long: `manage authorization policy`, + Short: "Manage authorization policy", + Long: `Manage authorization policy`, Run: runHelp, } diff --git a/pkg/cmd/experimental/policy/remove_group.go b/pkg/cmd/experimental/policy/remove_group.go index 38c434ec728a..b013f8e06070 100644 --- a/pkg/cmd/experimental/policy/remove_group.go +++ b/pkg/cmd/experimental/policy/remove_group.go @@ -24,7 +24,7 @@ func NewCmdRemoveGroup(f *clientcmd.Factory) *cobra.Command { options := &RemoveGroupOptions{} cmd := &cobra.Command{ - Use: "remove-group [group]...", + Use: "remove-role-from-group [group]...", Short: "remove group from role", Long: `remove group from role`, Run: func(cmd *cobra.Command, args []string) { diff --git a/pkg/cmd/experimental/policy/remove_group_from_project.go b/pkg/cmd/experimental/policy/remove_group_from_project.go index 93aefed43f2d..53b624cfe2e7 100644 --- a/pkg/cmd/experimental/policy/remove_group_from_project.go +++ b/pkg/cmd/experimental/policy/remove_group_from_project.go @@ -22,7 +22,7 @@ func NewCmdRemoveGroupFromProject(f *clientcmd.Factory) *cobra.Command { options := &removeGroupFromProjectOptions{} cmd := &cobra.Command{ - Use: "remove-group-from-project [group]...", + Use: "remove-group [group]...", Short: "remove group from project", Long: `remove group from project`, Run: func(cmd *cobra.Command, args []string) { diff --git a/pkg/cmd/experimental/policy/remove_user.go b/pkg/cmd/experimental/policy/remove_user.go index 0813919a8dce..958d55d4b891 100644 --- a/pkg/cmd/experimental/policy/remove_user.go +++ b/pkg/cmd/experimental/policy/remove_user.go @@ -24,7 +24,7 @@ func NewCmdRemoveUser(f *clientcmd.Factory) *cobra.Command { options := &removeUserOptions{} cmd := &cobra.Command{ - Use: "remove-user [user]...", + Use: "remove-role-from-user [user]...", Short: "remove user from role", Long: `remove user from role`, Run: func(cmd *cobra.Command, args []string) { diff --git a/pkg/cmd/experimental/policy/remove_user_from_project.go b/pkg/cmd/experimental/policy/remove_user_from_project.go index 1b66762a616d..40fa82d26ab9 100644 --- a/pkg/cmd/experimental/policy/remove_user_from_project.go +++ b/pkg/cmd/experimental/policy/remove_user_from_project.go @@ -22,7 +22,7 @@ func NewCmdRemoveUserFromProject(f *clientcmd.Factory) *cobra.Command { options := &removeUserFromProjectOptions{} cmd := &cobra.Command{ - Use: "remove-user-from-project [user]...", + Use: "remove-user [user]...", Short: "remove user from project", Long: `remove user from project`, Run: func(cmd *cobra.Command, args []string) { diff --git a/pkg/cmd/openshift/openshift.go b/pkg/cmd/openshift/openshift.go index fc666914c76b..a017ef2b75f3 100644 --- a/pkg/cmd/openshift/openshift.go +++ b/pkg/cmd/openshift/openshift.go @@ -6,6 +6,7 @@ import ( "github.com/spf13/cobra" + "github.com/openshift/origin/pkg/cmd/admin" "github.com/openshift/origin/pkg/cmd/cli" "github.com/openshift/origin/pkg/cmd/experimental/buildchain" "github.com/openshift/origin/pkg/cmd/experimental/config" @@ -19,7 +20,6 @@ import ( "github.com/openshift/origin/pkg/cmd/infra/builder" "github.com/openshift/origin/pkg/cmd/infra/deployer" "github.com/openshift/origin/pkg/cmd/infra/router" - "github.com/openshift/origin/pkg/cmd/server/admin" "github.com/openshift/origin/pkg/cmd/server/start" "github.com/openshift/origin/pkg/cmd/templates" "github.com/openshift/origin/pkg/cmd/util/clientcmd" @@ -27,7 +27,7 @@ import ( ) const longDescription = ` -OpenShift for Admins +OpenShift Application Platform OpenShift helps you build, deploy, and manage your applications. To start an all-in-one server, run: @@ -56,6 +56,8 @@ func CommandFor(basename string) *cobra.Command { cmd = builder.NewCommandDockerBuilder(basename) case "osc": cmd = cli.NewCommandCLI(basename, basename) + case "osadm": + cmd = admin.NewCommandAdmin(basename, basename) default: cmd = NewCommandOpenShift() } @@ -81,7 +83,7 @@ func NewCommandOpenShift() *cobra.Command { startAllInOne, _ := start.NewCommandStartAllInOne() root.AddCommand(startAllInOne) - root.AddCommand(admin.NewCommandAdmin()) + root.AddCommand(admin.NewCommandAdmin("admin", "openshift admin")) root.AddCommand(cli.NewCommandCLI("cli", "openshift cli")) root.AddCommand(cli.NewCmdKubectl("kube")) root.AddCommand(newExperimentalCommand("openshift", "ex")) diff --git a/pkg/cmd/server/admin/create_comands.go b/pkg/cmd/server/admin/create_comands.go deleted file mode 100644 index 3a50642086f9..000000000000 --- a/pkg/cmd/server/admin/create_comands.go +++ /dev/null @@ -1,28 +0,0 @@ -package admin - -import ( - "github.com/spf13/cobra" -) - -func NewCommandAdmin() *cobra.Command { - cmd := &cobra.Command{ - Use: "admin", - Short: "Admin commands", - Run: func(c *cobra.Command, args []string) { - c.Help() - }, - } - - cmd.AddCommand(NewCommandOverwriteBootstrapPolicy()) - cmd.AddCommand(NewCommandCreateBootstrapPolicyFile()) - cmd.AddCommand(NewCommandNodeConfig()) - cmd.AddCommand(NewCommandCreateKubeConfig()) - cmd.AddCommand(NewCommandCreateAllCerts()) - cmd.AddCommand(NewCommandCreateClientCert()) - cmd.AddCommand(NewCommandCreateNodeClientCert()) - cmd.AddCommand(NewCommandCreateServerCert()) - cmd.AddCommand(NewCommandCreateSignerCert()) - cmd.AddCommand(NewCommandCreateClient()) - - return cmd -} diff --git a/pkg/cmd/server/admin/overwrite_bootstrappolicy.go b/pkg/cmd/server/admin/overwrite_bootstrappolicy.go index e5bdef7812a6..47a1d133106f 100644 --- a/pkg/cmd/server/admin/overwrite_bootstrappolicy.go +++ b/pkg/cmd/server/admin/overwrite_bootstrappolicy.go @@ -3,6 +3,7 @@ package admin import ( "errors" "fmt" + "io" "github.com/golang/glog" "github.com/spf13/cobra" @@ -19,6 +20,7 @@ import ( authorizationetcd "github.com/openshift/origin/pkg/authorization/registry/etcd" roleregistry "github.com/openshift/origin/pkg/authorization/registry/role" rolebindingregistry "github.com/openshift/origin/pkg/authorization/registry/rolebinding" + "github.com/openshift/origin/pkg/cmd/cli/describe" configapilatest "github.com/openshift/origin/pkg/cmd/server/api/latest" configvalidation "github.com/openshift/origin/pkg/cmd/server/api/validation" "github.com/openshift/origin/pkg/cmd/server/etcd" @@ -29,14 +31,16 @@ import ( type OverwriteBootstrapPolicyOptions struct { File string MasterConfigFile string + Force bool + Out io.Writer } -func NewCommandOverwriteBootstrapPolicy() *cobra.Command { - options := &OverwriteBootstrapPolicyOptions{} +func NewCommandOverwriteBootstrapPolicy(out io.Writer) *cobra.Command { + options := &OverwriteBootstrapPolicyOptions{Out: out} cmd := &cobra.Command{ Use: "overwrite-policy", - Short: "Overwrite policy for OpenShift. DANGER: THIS BYPASSES ALL ACCESS CONTROL CHECKS AND WRITES DIRECTLY TO ETCD!", + Short: "Reset the policy to the default values", Run: func(c *cobra.Command, args []string) { if err := options.Validate(args); err != nil { fmt.Println(err.Error()) @@ -52,6 +56,7 @@ func NewCommandOverwriteBootstrapPolicy() *cobra.Command { flags := cmd.Flags() + flags.BoolVarP(&options.Force, "force", "f", false, "You must confirm you really want to reset your policy. This will delete any custom settings you may have.") flags.StringVar(&options.File, "filename", "", "The policy template file containing roles and bindings. One can be created with '"+CreateBootstrapPolicyFileFullCommand+"'.") flags.StringVar(&options.MasterConfigFile, "master-config", "master.yaml", "Location of the master configuration file to run from in order to connect to etcd and directly modify the policy.") @@ -86,10 +91,13 @@ func (o OverwriteBootstrapPolicyOptions) OverwriteBootstrapPolicy() error { return err } - return OverwriteBootstrapPolicy(etcdHelper, masterConfig.PolicyConfig.MasterAuthorizationNamespace, o.File) + return OverwriteBootstrapPolicy(etcdHelper, masterConfig.PolicyConfig.MasterAuthorizationNamespace, o.File, o.Force, o.Out) } -func OverwriteBootstrapPolicy(etcdHelper tools.EtcdHelper, masterNamespace, policyFile string) error { +func OverwriteBootstrapPolicy(etcdHelper tools.EtcdHelper, masterNamespace, policyFile string, change bool, out io.Writer) error { + if !change { + fmt.Fprintf(out, "Performing a dry run of policy overwrite:\n\n") + } mapper := cmdclientcmd.ShortcutExpander{kubectl.ShortcutExpander{latest.RESTMapper}} typer := api.Scheme clientMapper := resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) { @@ -116,26 +124,41 @@ func OverwriteBootstrapPolicy(etcdHelper tools.EtcdHelper, masterNamespace, poli } for _, item := range template.Objects { - switch castObject := item.(type) { + switch t := item.(type) { case *authorizationapi.Role: - ctx := api.WithNamespace(api.NewContext(), castObject.Namespace) - roleRegistry.DeleteRole(ctx, castObject.Name) - if err := roleRegistry.CreateRole(ctx, castObject); err != nil { - return err + ctx := api.WithNamespace(api.NewContext(), t.Namespace) + if change { + roleRegistry.DeleteRole(ctx, t.Name) + if err := roleRegistry.CreateRole(ctx, t); err != nil { + return err + } + } else { + fmt.Fprintf(out, "Overwrite role %s/%s\n", t.Namespace, t.Name) + if s, err := describe.DescribeRole(t); err == nil { + fmt.Fprintf(out, "%s\n", s) + } } - case *authorizationapi.RoleBinding: - ctx := api.WithNamespace(api.NewContext(), castObject.Namespace) - roleBindingRegistry.DeleteRoleBinding(ctx, castObject.Name) - if err := roleBindingRegistry.CreateRoleBinding(ctx, castObject, true); err != nil { - return err + ctx := api.WithNamespace(api.NewContext(), t.Namespace) + if change { + roleBindingRegistry.DeleteRoleBinding(ctx, t.Name) + if err := roleBindingRegistry.CreateRoleBinding(ctx, t, true); err != nil { + return err + } + } else { + fmt.Fprintf(out, "Overwrite role binding %s/%s\n", t.Namespace, t.Name) + if s, err := describe.DescribeRoleBinding(t, nil, nil); err == nil { + fmt.Fprintf(out, "%s\n", s) + } } default: return errors.New("only roles and rolebindings may be created in this mode") } } - + if !change { + fmt.Fprintf(out, "To make the changes described above, pass --force\n") + } return nil }) } diff --git a/pkg/cmd/server/origin/master.go b/pkg/cmd/server/origin/master.go index 575a15433d68..cbd6496d0ecc 100644 --- a/pkg/cmd/server/origin/master.go +++ b/pkg/cmd/server/origin/master.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "io/ioutil" "net" "net/http" "net/url" @@ -390,7 +391,7 @@ func (c *MasterConfig) ensureComponentAuthorizationRules() { if _, err := registry.GetPolicy(ctx, authorizationapi.PolicyName); kapierror.IsNotFound(err) { glog.Infof("No master policy found. Creating bootstrap policy based on: %v", c.Options.PolicyConfig.BootstrapPolicyFile) - if err := admin.OverwriteBootstrapPolicy(c.EtcdHelper, c.Options.PolicyConfig.MasterAuthorizationNamespace, c.Options.PolicyConfig.BootstrapPolicyFile); err != nil { + if err := admin.OverwriteBootstrapPolicy(c.EtcdHelper, c.Options.PolicyConfig.MasterAuthorizationNamespace, c.Options.PolicyConfig.BootstrapPolicyFile, true, ioutil.Discard); err != nil { glog.Errorf("Error creating bootstrap policy: %v", err) } diff --git a/pkg/cmd/templates/templater.go b/pkg/cmd/templates/templater.go index 85e4a3abdac1..a5d3c0aae88d 100644 --- a/pkg/cmd/templates/templater.go +++ b/pkg/cmd/templates/templater.go @@ -50,6 +50,12 @@ func UseCliTemplates(cmd *cobra.Command) { cmd.SetUsageFunc(templater.UsageFunc()) } +func UseAdminTemplates(cmd *cobra.Command) { + cmd.SetHelpTemplate(AdminHelpTemplate()) + templater := &Templater{UsageTemplate: AdminUsageTemplate()} + cmd.SetUsageFunc(templater.UsageFunc()) +} + func UseMainTemplates(cmd *cobra.Command) { cmd.SetHelpTemplate(MainHelpTemplate()) templater := &Templater{UsageTemplate: MainUsageTemplate()} diff --git a/pkg/cmd/templates/templates.go b/pkg/cmd/templates/templates.go index eb4236ff795b..dd8bf17a8d27 100644 --- a/pkg/cmd/templates/templates.go +++ b/pkg/cmd/templates/templates.go @@ -18,6 +18,14 @@ func CliUsageTemplate() string { return decorate(cliUsageTemplate, true) } +func AdminHelpTemplate() string { + return decorate(adminHelpTemplate, false) +} + +func AdminUsageTemplate() string { + return decorate(adminUsageTemplate, true) +} + func OptionsHelpTemplate() string { return decorate(optionsHelpTemplate, false) } @@ -35,7 +43,8 @@ func decorate(template string, trim bool) string { } const ( - funcs = `{{$isRootCmd := or (and (eq .Name "cli") (eq .Root.Name "openshift")) (eq .Name "osc")}}{{define "rootCli"}}{{if eq .Root.Name "osc"}}osc{{else}}openshift cli{{end}}{{end}}` + // TODO: $isRootCmd should be done in code, not in the template + funcs = `{{$isRootCmd := or (and (eq .Name "cli") (eq .Root.Name "openshift")) (eq .Name "osc") (and (eq .Name "admin") (eq .Root.Name "openshift")) (eq .Name "osadm")}}{{define "rootCli"}}{{if eq .Root.Name "osadm"}}osadm{{else}}{{if eq .Root.Name "osc"}}osc{{else}}openshift {{.Name}}{{end}}{{end}}{{end}}` mainHelpTemplate = `{{.Long | trim}} {{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` @@ -70,6 +79,20 @@ Available Commands: {{range .Commands}}{{if .Runnable}}{{if ne .Name "options"}} {{end}}{{ if not $isRootCmd}}Use "{{template "rootCli" .}} --help" for a list of all commands available in {{template "rootCli" .}}. {{end}}{{ if .HasSubCommands }}Use "{{template "rootCli" .}} --help" for more information about a given command. {{end}}{{ if .HasAnyPersistentFlags}}Use "{{template "rootCli" .}} options" for a list of global command-line options (applies to all commands). +{{end}}` + + adminHelpTemplate = `{{.Long | trim}} +{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` + + adminUsageTemplate = `{{ $cmd := . }}{{$exposedFlags := exposed .}}{{ if .HasSubCommands}} +Available Commands: {{range .Commands}}{{if .Runnable}}{{if ne .Name "options"}} + {{rpad .Name 20 }}{{.Short}}{{end}}{{end}}{{end}} +{{end}} +{{ if or .HasLocalFlags $exposedFlags.HasFlags}}Options: +{{ if .HasLocalFlags}}{{.LocalFlags.FlagUsages}}{{end}}{{ if $exposedFlags.HasFlags}}{{$exposedFlags.FlagUsages}}{{end}} +{{end}}{{ if not $isRootCmd}}Use "{{template "rootCli" .}} --help" for a list of all commands available in {{template "rootCli" .}}. +{{end}}{{ if .HasSubCommands }}Use "{{template "rootCli" .}} --help" for more information about a given command. +{{end}}{{ if .HasAnyPersistentFlags}}Use "{{template "rootCli" .}} options" for a list of global command-line options (applies to all commands). {{end}}` optionsHelpTemplate = `{{ if .HasAnyPersistentFlags}}The following options can be passed to any command: diff --git a/test/integration/bootstrap_policy_test.go b/test/integration/bootstrap_policy_test.go index e88c06c216a3..daf1af9b38aa 100644 --- a/test/integration/bootstrap_policy_test.go +++ b/test/integration/bootstrap_policy_test.go @@ -3,6 +3,7 @@ package integration import ( + "io/ioutil" "testing" kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" @@ -98,7 +99,7 @@ func TestOverwritePolicyCommand(t *testing.T) { t.Errorf("unexpected error: %v", err) } - if err := admin.OverwriteBootstrapPolicy(etcdHelper, masterConfig.PolicyConfig.MasterAuthorizationNamespace, masterConfig.PolicyConfig.BootstrapPolicyFile); err != nil { + if err := admin.OverwriteBootstrapPolicy(etcdHelper, masterConfig.PolicyConfig.MasterAuthorizationNamespace, masterConfig.PolicyConfig.BootstrapPolicyFile, true, ioutil.Discard); err != nil { t.Errorf("unexpected error: %v", err) }