| ... | ... |
@@ -45,7 +45,8 @@ TEMP_DIR=${USE_TEMP:-$(mktemp -d /tmp/openshift-cmd.XXXX)}
|
| 45 | 45 |
ETCD_DATA_DIR="${TEMP_DIR}/etcd"
|
| 46 | 46 |
VOLUME_DIR="${TEMP_DIR}/volumes"
|
| 47 | 47 |
CERT_DIR="${TEMP_DIR}/certs"
|
| 48 |
-mkdir -p "${ETCD_DATA_DIR}" "${VOLUME_DIR}" "${CERT_DIR}"
|
|
| 48 |
+CONFIG_DIR="${TEMP_DIR}/configs"
|
|
| 49 |
+mkdir -p "${ETCD_DATA_DIR}" "${VOLUME_DIR}" "${CERT_DIR}" "${CONFIG_DIR}"
|
|
| 49 | 50 |
|
| 50 | 51 |
# handle profiling defaults |
| 51 | 52 |
profile="${OPENSHIFT_PROFILE-}"
|
| ... | ... |
@@ -83,14 +84,6 @@ wait_for_url "http://${API_HOST}:${KUBELET_PORT}/healthz" "kubelet: " 0.25 80
|
| 83 | 83 |
wait_for_url "${API_SCHEME}://${API_HOST}:${API_PORT}/healthz" "apiserver: " 0.25 80
|
| 84 | 84 |
wait_for_url "${API_SCHEME}://${API_HOST}:${API_PORT}/api/v1beta1/minions/127.0.0.1" "apiserver(minions): " 0.25 80
|
| 85 | 85 |
|
| 86 |
-# Set KUBERNETES_MASTER for osc |
|
| 87 |
-export KUBERNETES_MASTER="${API_SCHEME}://${API_HOST}:${API_PORT}"
|
|
| 88 |
-if [[ "${API_SCHEME}" == "https" ]]; then
|
|
| 89 |
- # Make osc use ${CERT_DIR}/admin/.kubeconfig, and ignore anything in the running user's $HOME dir
|
|
| 90 |
- export HOME="${CERT_DIR}/admin"
|
|
| 91 |
- export KUBECONFIG="${CERT_DIR}/admin/.kubeconfig"
|
|
| 92 |
-fi |
|
| 93 |
- |
|
| 94 | 86 |
# profile the cli commands |
| 95 | 87 |
export OPENSHIFT_PROFILE="${CLI_PROFILE-}"
|
| 96 | 88 |
|
| ... | ... |
@@ -98,6 +91,49 @@ export OPENSHIFT_PROFILE="${CLI_PROFILE-}"
|
| 98 | 98 |
# Begin tests |
| 99 | 99 |
# |
| 100 | 100 |
|
| 101 |
+# test client not configured |
|
| 102 |
+[ "$(osc get services 2>&1 | grep 'no server found')" ] |
|
| 103 |
+ |
|
| 104 |
+# Set KUBERNETES_MASTER for osc from now on |
|
| 105 |
+export KUBERNETES_MASTER="${API_SCHEME}://${API_HOST}:${API_PORT}"
|
|
| 106 |
+ |
|
| 107 |
+# Set certificates for osc from now on |
|
| 108 |
+if [[ "${API_SCHEME}" == "https" ]]; then
|
|
| 109 |
+ # test bad certificate |
|
| 110 |
+ [ "$(osc get services 2>&1 | grep 'certificate signed by unknown authority')" ] |
|
| 111 |
+ |
|
| 112 |
+ # ignore anything in the running user's $HOME dir |
|
| 113 |
+ export HOME="${CERT_DIR}/admin"
|
|
| 114 |
+fi |
|
| 115 |
+ |
|
| 116 |
+# test config files from the --config flag |
|
| 117 |
+osc get services --config="${CERT_DIR}/admin/.kubeconfig"
|
|
| 118 |
+ |
|
| 119 |
+# test config files from env vars |
|
| 120 |
+OPENSHIFTCONFIG="${CERT_DIR}/admin/.kubeconfig" osc get services
|
|
| 121 |
+KUBECONFIG="${CERT_DIR}/admin/.kubeconfig" osc get services
|
|
| 122 |
+ |
|
| 123 |
+# test config files in the current directory |
|
| 124 |
+TEMP_PWD=`pwd` |
|
| 125 |
+pushd ${CONFIG_DIR} >/dev/null
|
|
| 126 |
+ cp ${CERT_DIR}/admin/.kubeconfig .openshiftconfig
|
|
| 127 |
+ ${TEMP_PWD}/${GO_OUT}/osc get services
|
|
| 128 |
+ mv .openshiftconfig .kubeconfig |
|
| 129 |
+ ${TEMP_PWD}/${GO_OUT}/osc get services
|
|
| 130 |
+popd |
|
| 131 |
+ |
|
| 132 |
+# test config files in the home directory |
|
| 133 |
+mv ${CONFIG_DIR} ${HOME}/.kube
|
|
| 134 |
+osc get services |
|
| 135 |
+mkdir -p ${HOME}/.config
|
|
| 136 |
+mv ${HOME}/.kube ${HOME}/.config/openshift
|
|
| 137 |
+mv ${HOME}/.config/openshift/.kubeconfig ${HOME}/.config/openshift/.config
|
|
| 138 |
+osc get services |
|
| 139 |
+echo "config files: ok" |
|
| 140 |
+export OPENSHIFTCONFIG="${HOME}/.config/openshift/.config"
|
|
| 141 |
+ |
|
| 142 |
+# from this point every command will use config from the OPENSHIFTCONFIG env var |
|
| 143 |
+ |
|
| 101 | 144 |
osc get templates |
| 102 | 145 |
osc create -f examples/sample-app/application-template-dockerbuild.json |
| 103 | 146 |
osc get templates |
| ... | ... |
@@ -273,14 +309,14 @@ osc describe policybinding master -n recreated-project | grep anypassword:create |
| 273 | 273 |
echo "ex new-project: ok" |
| 274 | 274 |
|
| 275 | 275 |
[ ! "$(openshift ex router | grep 'does not exist')"] |
| 276 |
-[ "$(openshift ex router -o yaml --credentials="${KUBECONFIG}" | grep 'openshift/origin-haproxy-')" ]
|
|
| 277 |
-openshift ex router --create --credentials="${KUBECONFIG}"
|
|
| 276 |
+[ "$(openshift ex router -o yaml --credentials="${OPENSHIFTCONFIG}" | grep 'openshift/origin-haproxy-')" ]
|
|
| 277 |
+openshift ex router --create --credentials="${OPENSHIFTCONFIG}"
|
|
| 278 | 278 |
[ "$(openshift ex router | grep 'service exists')" ] |
| 279 | 279 |
echo "ex router: ok" |
| 280 | 280 |
|
| 281 | 281 |
[ ! "$(openshift ex registry | grep 'does not exist')"] |
| 282 |
-[ "$(openshift ex registry -o yaml --credentials="${KUBECONFIG}" | grep 'openshift/origin-docker-registry')" ]
|
|
| 283 |
-openshift ex registry --create --credentials="${KUBECONFIG}"
|
|
| 282 |
+[ "$(openshift ex registry -o yaml --credentials="${OPENSHIFTCONFIG}" | grep 'openshift/origin-docker-registry')" ]
|
|
| 283 |
+openshift ex registry --create --credentials="${OPENSHIFTCONFIG}"
|
|
| 284 | 284 |
[ "$(openshift ex registry | grep 'service exists')" ] |
| 285 | 285 |
echo "ex registry: ok" |
| 286 | 286 |
|
| ... | ... |
@@ -56,11 +56,13 @@ func NewCommandCLI(name, fullName string) *cobra.Command {
|
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 | 58 |
f := clientcmd.New(cmds.PersistentFlags()) |
| 59 |
+ in := os.Stdin |
|
| 59 | 60 |
out := os.Stdout |
| 60 | 61 |
|
| 61 | 62 |
cmds.SetUsageTemplate(templates.CliUsageTemplate()) |
| 62 | 63 |
cmds.SetHelpTemplate(templates.CliHelpTemplate()) |
| 63 | 64 |
|
| 65 |
+ cmds.AddCommand(cmd.NewCmdLogin(f, in, out)) |
|
| 64 | 66 |
cmds.AddCommand(cmd.NewCmdNewApplication(fullName, f, out)) |
| 65 | 67 |
cmds.AddCommand(cmd.NewCmdStartBuild(fullName, f, out)) |
| 66 | 68 |
cmds.AddCommand(cmd.NewCmdCancelBuild(fullName, f, out)) |
| ... | ... |
@@ -76,6 +78,7 @@ func NewCommandCLI(name, fullName string) *cobra.Command {
|
| 76 | 76 |
cmds.AddCommand(cmd.NewCmdLog(fullName, f, out)) |
| 77 | 77 |
cmds.AddCommand(f.NewCmdProxy(out)) |
| 78 | 78 |
cmds.AddCommand(kubecmd.NewCmdNamespace(out)) |
| 79 |
+ cmds.AddCommand(cmd.NewCmdProject(f, out)) |
|
| 79 | 80 |
cmds.AddCommand(cmd.NewCmdOptions(f, out)) |
| 80 | 81 |
cmds.AddCommand(version.NewVersionCommand(fullName)) |
| 81 | 82 |
|
| 82 | 83 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,56 @@ |
| 0 |
+package cmd |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/spf13/cobra" |
|
| 7 |
+ |
|
| 8 |
+ cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" |
|
| 9 |
+ "github.com/openshift/origin/pkg/cmd/cli/config" |
|
| 10 |
+ osclientcmd "github.com/openshift/origin/pkg/cmd/util/clientcmd" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+const longDescription = `Logs in to the OpenShift server and save the session |
|
| 14 |
+information to a config file that will be used by every subsequent command. |
|
| 15 |
+ |
|
| 16 |
+First-time users of the OpenShift client must run this command to configure the server, |
|
| 17 |
+establish a session against it and save it to a configuration file, usually in the |
|
| 18 |
+user's home directory. |
|
| 19 |
+ |
|
| 20 |
+The information required to login, like username and password or a session token, and |
|
| 21 |
+the server details, can be provided through flags. If not provided, the command will |
|
| 22 |
+prompt for user input if needed. |
|
| 23 |
+` |
|
| 24 |
+ |
|
| 25 |
+func NewCmdLogin(f *osclientcmd.Factory, reader io.Reader, out io.Writer) *cobra.Command {
|
|
| 26 |
+ options := &LoginOptions{}
|
|
| 27 |
+ |
|
| 28 |
+ cmds := &cobra.Command{
|
|
| 29 |
+ Use: "login [--username=<username>] [--password=<password>] [--server=<server>] [--context=<context>] [--certificate-authority=<path>]", |
|
| 30 |
+ Short: "Logs in and save the configuration", |
|
| 31 |
+ Long: longDescription, |
|
| 32 |
+ Run: func(cmd *cobra.Command, args []string) {
|
|
| 33 |
+ options.Reader = reader |
|
| 34 |
+ options.ClientConfig = f.OpenShiftClientConfig |
|
| 35 |
+ |
|
| 36 |
+ checkErr(options.GatherInfo()) |
|
| 37 |
+ |
|
| 38 |
+ forcePath := cmdutil.GetFlagString(cmd, config.OpenShiftConfigFlagName) |
|
| 39 |
+ options.PathToSaveConfig = forcePath |
|
| 40 |
+ |
|
| 41 |
+ newFileCreated, err := options.SaveConfig() |
|
| 42 |
+ checkErr(err) |
|
| 43 |
+ |
|
| 44 |
+ if newFileCreated {
|
|
| 45 |
+ fmt.Println("Welcome to OpenShift v3! Use 'osc --help' for a list of commands available.")
|
|
| 46 |
+ } |
|
| 47 |
+ }, |
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ // TODO flags below should be DE-REGISTERED from the persistent flags and kept only here. |
|
| 51 |
+ // Login is the only command that can negotiate a session token against the auth server. |
|
| 52 |
+ cmds.Flags().StringVarP(&options.Username, "username", "u", "", "Username, will prompt if not provided") |
|
| 53 |
+ cmds.Flags().StringVarP(&options.Password, "password", "p", "", "Password, will prompt if not provided") |
|
| 54 |
+ return cmds |
|
| 55 |
+} |
| 0 | 56 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,334 @@ |
| 0 |
+package cmd |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io" |
|
| 5 |
+ "os" |
|
| 6 |
+ "sort" |
|
| 7 |
+ "strings" |
|
| 8 |
+ |
|
| 9 |
+ kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" |
|
| 10 |
+ kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
| 11 |
+ kclientcmd "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" |
|
| 12 |
+ clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" |
|
| 13 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 14 |
+ |
|
| 15 |
+ "github.com/openshift/origin/pkg/client" |
|
| 16 |
+ "github.com/openshift/origin/pkg/cmd/cli/config" |
|
| 17 |
+ "github.com/openshift/origin/pkg/cmd/util" |
|
| 18 |
+ "github.com/openshift/origin/pkg/cmd/util/clientcmd" |
|
| 19 |
+ "github.com/openshift/origin/pkg/cmd/util/tokencmd" |
|
| 20 |
+ "github.com/openshift/origin/pkg/user/api" |
|
| 21 |
+) |
|
| 22 |
+ |
|
| 23 |
+const defaultClusterURL = "https://localhost:8443" |
|
| 24 |
+ |
|
| 25 |
+// Helper for the login and setup process, gathers all information required for a |
|
| 26 |
+// successful login and eventual update of config files. |
|
| 27 |
+// Depending on the Reader present it can be interactive, asking for terminal input in |
|
| 28 |
+// case of any missing information. |
|
| 29 |
+// Notice that some methods mutate this object so it should not be reused. The Config |
|
| 30 |
+// provided as a pointer will also mutate (handle new auth tokens, etc). |
|
| 31 |
+type LoginOptions struct {
|
|
| 32 |
+ // flags and printing helpers |
|
| 33 |
+ Username string |
|
| 34 |
+ Password string |
|
| 35 |
+ Project string |
|
| 36 |
+ |
|
| 37 |
+ // infra |
|
| 38 |
+ ClientConfig kclientcmd.ClientConfig |
|
| 39 |
+ Config *kclient.Config |
|
| 40 |
+ Reader io.Reader |
|
| 41 |
+ |
|
| 42 |
+ // flow controllers |
|
| 43 |
+ gatherServerInfo bool |
|
| 44 |
+ gatherAuthInfo bool |
|
| 45 |
+ gatherProjectInfo bool |
|
| 46 |
+ |
|
| 47 |
+ // Optional, if provided will only try to save in it |
|
| 48 |
+ PathToSaveConfig string |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+// Gather all required information in a comprehensive order. |
|
| 52 |
+func (o *LoginOptions) GatherInfo() error {
|
|
| 53 |
+ if err := o.GatherServerInfo(); err != nil {
|
|
| 54 |
+ return err |
|
| 55 |
+ } |
|
| 56 |
+ if err := o.GatherAuthInfo(); err != nil {
|
|
| 57 |
+ return err |
|
| 58 |
+ } |
|
| 59 |
+ if err := o.GatherProjectInfo(); err != nil {
|
|
| 60 |
+ return err |
|
| 61 |
+ } |
|
| 62 |
+ return nil |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+// Makes sure it has all the needed information about the server we are connecting to, |
|
| 66 |
+// particularly the host address and certificate information. For every information not |
|
| 67 |
+// present ask for interactive user input. Will also ping the server to make sure we can |
|
| 68 |
+// connect to it, and if any problem is found (e.g. certificate issues), ask the user about |
|
| 69 |
+// connecting insecurely. |
|
| 70 |
+func (o *LoginOptions) GatherServerInfo() error {
|
|
| 71 |
+ // we need to have a server to talk to |
|
| 72 |
+ |
|
| 73 |
+ if util.IsTerminal(o.Reader) {
|
|
| 74 |
+ for !o.serverProvided() {
|
|
| 75 |
+ defaultServer := defaultClusterURL |
|
| 76 |
+ promptMsg := fmt.Sprintf("Please provide the server URL or just <enter> to use '%v': ", defaultServer)
|
|
| 77 |
+ |
|
| 78 |
+ server := util.PromptForStringWithDefault(o.Reader, defaultServer, promptMsg) |
|
| 79 |
+ kclientcmd.DefaultCluster = clientcmdapi.Cluster{Server: server}
|
|
| 80 |
+ } |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ // we know the server we are expected to use |
|
| 84 |
+ |
|
| 85 |
+ clientCfg, err := o.ClientConfig.ClientConfig() |
|
| 86 |
+ if err != nil {
|
|
| 87 |
+ return err |
|
| 88 |
+ } |
|
| 89 |
+ |
|
| 90 |
+ // ping to check if server is reachable |
|
| 91 |
+ |
|
| 92 |
+ osClient, err := client.New(clientCfg) |
|
| 93 |
+ if err != nil {
|
|
| 94 |
+ return err |
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 97 |
+ result := osClient.Get().AbsPath("/osapi").Do()
|
|
| 98 |
+ if result.Error() != nil {
|
|
| 99 |
+ // certificate issue, prompt user for insecure connection |
|
| 100 |
+ |
|
| 101 |
+ if clientcmd.IsCertificateAuthorityUnknown(result.Error()) {
|
|
| 102 |
+ fmt.Println("The server uses a certificate signed by unknown authority. You can bypass the certificate check but it will make all connections insecure.")
|
|
| 103 |
+ |
|
| 104 |
+ clientCfg.Insecure = util.PromptForBool(os.Stdin, "Use insecure connections (strongly discouraged)? [y/N] ") |
|
| 105 |
+ if !clientCfg.Insecure {
|
|
| 106 |
+ return fmt.Errorf(clientcmd.GetPrettyMessageFor(result.Error())) |
|
| 107 |
+ } |
|
| 108 |
+ } |
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+ // we have all info we need, now we can have a proper Config |
|
| 112 |
+ |
|
| 113 |
+ o.Config = clientCfg |
|
| 114 |
+ |
|
| 115 |
+ o.gatherServerInfo = true |
|
| 116 |
+ return nil |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+// Negotiate a bearer token with the auth server, or try to reuse one based on the |
|
| 120 |
+// information already present. In case of any missing information, ask for user input |
|
| 121 |
+// (usually username and password, interactive depending on the Reader). |
|
| 122 |
+func (o *LoginOptions) GatherAuthInfo() error {
|
|
| 123 |
+ if err := o.assertGatheredServerInfo(); err != nil {
|
|
| 124 |
+ return err |
|
| 125 |
+ } |
|
| 126 |
+ |
|
| 127 |
+ if me, err := o.Whoami(); err == nil && (!o.usernameProvided() || (o.usernameProvided() && o.Username == usernameFromUser(me))) {
|
|
| 128 |
+ o.Username = usernameFromUser(me) |
|
| 129 |
+ fmt.Printf("Already logged into '%v' as '%v'.\n", o.Config.Host, o.Username)
|
|
| 130 |
+ |
|
| 131 |
+ } else {
|
|
| 132 |
+ // if not, we need to log in again |
|
| 133 |
+ |
|
| 134 |
+ o.Config.BearerToken = "" |
|
| 135 |
+ token, err := tokencmd.RequestToken(o.Config, o.Reader, o.Username, o.Password) |
|
| 136 |
+ if err != nil {
|
|
| 137 |
+ return err |
|
| 138 |
+ } |
|
| 139 |
+ o.Config.BearerToken = token |
|
| 140 |
+ |
|
| 141 |
+ me, err := o.Whoami() |
|
| 142 |
+ if err != nil {
|
|
| 143 |
+ return err |
|
| 144 |
+ } |
|
| 145 |
+ o.Username = usernameFromUser(me) |
|
| 146 |
+ fmt.Printf("Logged into '%v' as '%v'.\n", o.Config.Host, o.Username)
|
|
| 147 |
+ } |
|
| 148 |
+ |
|
| 149 |
+ // TODO investigate about the safety and intent of the proposal below |
|
| 150 |
+ // if trying to log in an user that's not the currently logged in, try to reuse an existing token |
|
| 151 |
+ |
|
| 152 |
+ // if o.usernameProvided() {
|
|
| 153 |
+ // glog.V(5).Infof("Checking existing authentication info for '%v'...\n", o.Username)
|
|
| 154 |
+ |
|
| 155 |
+ // for _, ctx := range rawCfg.Contexts {
|
|
| 156 |
+ // authInfo := rawCfg.AuthInfos[ctx.AuthInfo] |
|
| 157 |
+ // clusterInfo := rawCfg.Clusters[ctx.Cluster] |
|
| 158 |
+ |
|
| 159 |
+ // if ctx.AuthInfo == o.Username && clusterInfo.Server == o.Server && len(authInfo.Token) > 0 { // only token for now
|
|
| 160 |
+ // glog.V(5).Infof("Authentication exists for '%v' on '%v', trying to use it...\n", o.Server, o.Username)
|
|
| 161 |
+ |
|
| 162 |
+ // o.Config.BearerToken = authInfo.Token |
|
| 163 |
+ |
|
| 164 |
+ // if me, err := whoami(o.Config); err == nil && usernameFromUser(me) == o.Username {
|
|
| 165 |
+ // o.Username = usernameFromUser(me) |
|
| 166 |
+ // return nil |
|
| 167 |
+ // } |
|
| 168 |
+ |
|
| 169 |
+ // glog.V(5).Infof("Token %v no longer valid for '%v', can't use it\n", authInfo.Token, o.Username)
|
|
| 170 |
+ // } |
|
| 171 |
+ // } |
|
| 172 |
+ // } |
|
| 173 |
+ |
|
| 174 |
+ o.gatherAuthInfo = true |
|
| 175 |
+ return nil |
|
| 176 |
+} |
|
| 177 |
+ |
|
| 178 |
+// Discover the projects available for the stabilished session and take one to use. It |
|
| 179 |
+// fails in case of no existing projects, and print out useful information in case of |
|
| 180 |
+// multiple projects. |
|
| 181 |
+func (o *LoginOptions) GatherProjectInfo() error {
|
|
| 182 |
+ if err := o.assertGatheredAuthInfo(); err != nil {
|
|
| 183 |
+ return err |
|
| 184 |
+ } |
|
| 185 |
+ |
|
| 186 |
+ oClient, err := client.New(o.Config) |
|
| 187 |
+ if err != nil {
|
|
| 188 |
+ return err |
|
| 189 |
+ } |
|
| 190 |
+ |
|
| 191 |
+ projects, err := oClient.Projects().List(labels.Everything(), labels.Everything()) |
|
| 192 |
+ if err != nil {
|
|
| 193 |
+ return err |
|
| 194 |
+ } |
|
| 195 |
+ |
|
| 196 |
+ projectsItems := projects.Items |
|
| 197 |
+ |
|
| 198 |
+ switch len(projectsItems) {
|
|
| 199 |
+ case 0: |
|
| 200 |
+ if me, err := o.Whoami(); err == nil {
|
|
| 201 |
+ // TODO most users will not be allowed to run the suggested commands below, so we should check it and/or |
|
| 202 |
+ // have a server endpoint that allows an admin to describe to users how to request projects |
|
| 203 |
+ return fmt.Errorf(`You don't have any project. |
|
| 204 |
+To create a new project, run 'openshift ex new-project <projectname> --admin=%s'. |
|
| 205 |
+To be added as an admin to an existing project, run 'openshift ex policy add-user admin %s -n <projectname>'.`, me.Name, me.Name) |
|
| 206 |
+ } |
|
| 207 |
+ return err |
|
| 208 |
+ |
|
| 209 |
+ case 1: |
|
| 210 |
+ o.Project = projectsItems[0].Name |
|
| 211 |
+ |
|
| 212 |
+ default: |
|
| 213 |
+ projects := []string{}
|
|
| 214 |
+ |
|
| 215 |
+ for _, project := range projectsItems {
|
|
| 216 |
+ projects = append(projects, project.Name) |
|
| 217 |
+ } |
|
| 218 |
+ |
|
| 219 |
+ namespace, err := o.ClientConfig.Namespace() |
|
| 220 |
+ if err != nil {
|
|
| 221 |
+ return err |
|
| 222 |
+ } |
|
| 223 |
+ |
|
| 224 |
+ if current, err := oClient.Projects().Get(namespace); err != nil {
|
|
| 225 |
+ if kerrors.IsNotFound(err) || kerrors.IsForbidden(err) {
|
|
| 226 |
+ o.Project = projects[0] |
|
| 227 |
+ } else {
|
|
| 228 |
+ return err |
|
| 229 |
+ } |
|
| 230 |
+ } else {
|
|
| 231 |
+ o.Project = current.Name |
|
| 232 |
+ } |
|
| 233 |
+ |
|
| 234 |
+ if n := len(projects); n > 10 {
|
|
| 235 |
+ projects = projects[:10] |
|
| 236 |
+ fmt.Printf("You have %d projects, displaying only the first 10. To view all your projects run 'osc get projects'.\n", n)
|
|
| 237 |
+ } |
|
| 238 |
+ var sortedProjects sort.StringSlice = projects |
|
| 239 |
+ sortedProjects.Sort() |
|
| 240 |
+ fmt.Printf("Your projects are: %v. You can switch between them at any time using 'osc project <project-name>'.\n", strings.Join(projects, ", "))
|
|
| 241 |
+ } |
|
| 242 |
+ |
|
| 243 |
+ fmt.Printf("Using project '%v'.\n", o.Project)
|
|
| 244 |
+ |
|
| 245 |
+ o.gatherProjectInfo = true |
|
| 246 |
+ return nil |
|
| 247 |
+} |
|
| 248 |
+ |
|
| 249 |
+// Save all the information present in this helper to a config file. An explicit config |
|
| 250 |
+// file path can be provided, if not use the established conventions about config |
|
| 251 |
+// loading rules. Will create a new config file if one can't be found at all. Will only |
|
| 252 |
+// succeed if all required info is present. |
|
| 253 |
+func (o *LoginOptions) SaveConfig() (created bool, err error) {
|
|
| 254 |
+ if len(o.Project) == 0 || len(o.Username) == 0 {
|
|
| 255 |
+ return false, fmt.Errorf("Insufficient data to merge configuration.")
|
|
| 256 |
+ } |
|
| 257 |
+ |
|
| 258 |
+ var configStore *config.ConfigStore |
|
| 259 |
+ |
|
| 260 |
+ if len(o.PathToSaveConfig) > 0 {
|
|
| 261 |
+ configStore, err = config.LoadFrom(o.PathToSaveConfig) |
|
| 262 |
+ if err != nil {
|
|
| 263 |
+ return created, err |
|
| 264 |
+ } |
|
| 265 |
+ } else {
|
|
| 266 |
+ configStore, err = config.LoadWithLoadingRules() |
|
| 267 |
+ if err != nil {
|
|
| 268 |
+ configStore, err = config.CreateEmpty() |
|
| 269 |
+ if err != nil {
|
|
| 270 |
+ return created, err |
|
| 271 |
+ } |
|
| 272 |
+ created = true |
|
| 273 |
+ } |
|
| 274 |
+ } |
|
| 275 |
+ |
|
| 276 |
+ rawCfg, err := o.ClientConfig.RawConfig() |
|
| 277 |
+ if err != nil {
|
|
| 278 |
+ return created, err |
|
| 279 |
+ } |
|
| 280 |
+ return created, configStore.SaveToFile(o.Username, o.Project, o.Config, rawCfg) |
|
| 281 |
+} |
|
| 282 |
+ |
|
| 283 |
+func (o *LoginOptions) Whoami() (*api.User, error) {
|
|
| 284 |
+ oClient, err := client.New(o.Config) |
|
| 285 |
+ if err != nil {
|
|
| 286 |
+ return nil, err |
|
| 287 |
+ } |
|
| 288 |
+ |
|
| 289 |
+ me, err := oClient.Users().Get("~")
|
|
| 290 |
+ if err != nil {
|
|
| 291 |
+ return nil, err |
|
| 292 |
+ } |
|
| 293 |
+ |
|
| 294 |
+ return me, nil |
|
| 295 |
+} |
|
| 296 |
+ |
|
| 297 |
+func (o *LoginOptions) assertGatheredServerInfo() error {
|
|
| 298 |
+ if !o.gatherServerInfo {
|
|
| 299 |
+ return fmt.Errorf("Must gather server info first.")
|
|
| 300 |
+ } |
|
| 301 |
+ return nil |
|
| 302 |
+} |
|
| 303 |
+ |
|
| 304 |
+func (o *LoginOptions) assertGatheredAuthInfo() error {
|
|
| 305 |
+ if !o.gatherAuthInfo {
|
|
| 306 |
+ return fmt.Errorf("Must gather auth info first.")
|
|
| 307 |
+ } |
|
| 308 |
+ return nil |
|
| 309 |
+} |
|
| 310 |
+ |
|
| 311 |
+func (o *LoginOptions) assertGatheredProjectInfo() error {
|
|
| 312 |
+ if !o.gatherProjectInfo {
|
|
| 313 |
+ return fmt.Errorf("Must gather project info first.")
|
|
| 314 |
+ } |
|
| 315 |
+ return nil |
|
| 316 |
+} |
|
| 317 |
+ |
|
| 318 |
+func (o *LoginOptions) usernameProvided() bool {
|
|
| 319 |
+ return len(o.Username) > 0 |
|
| 320 |
+} |
|
| 321 |
+ |
|
| 322 |
+func (o *LoginOptions) passwordProvided() bool {
|
|
| 323 |
+ return len(o.Password) > 0 |
|
| 324 |
+} |
|
| 325 |
+ |
|
| 326 |
+func (o *LoginOptions) serverProvided() bool {
|
|
| 327 |
+ _, err := o.ClientConfig.ClientConfig() |
|
| 328 |
+ return err == nil || !clientcmd.IsNoServerFound(err) |
|
| 329 |
+} |
|
| 330 |
+ |
|
| 331 |
+func usernameFromUser(user *api.User) string {
|
|
| 332 |
+ return strings.Split(user.Name, ":")[1] |
|
| 333 |
+} |
| ... | ... |
@@ -64,7 +64,12 @@ func NewCmdProject(f *clientcmd.Factory, out io.Writer) *cobra.Command {
|
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 | 66 |
pathFromFlag := cmdutil.GetFlagString(cmd, config.OpenShiftConfigFlagName) |
| 67 |
- configStore, err := config.Load(clientCfg, pathFromFlag) |
|
| 67 |
+ |
|
| 68 |
+ configStore, err := config.LoadFrom(pathFromFlag) |
|
| 69 |
+ if err != nil {
|
|
| 70 |
+ configStore, err = config.LoadWithLoadingRules() |
|
| 71 |
+ checkErr(err) |
|
| 72 |
+ } |
|
| 68 | 73 |
checkErr(err) |
| 69 | 74 |
|
| 70 | 75 |
config := configStore.Config |
| 71 | 76 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,54 @@ |
| 0 |
+package config |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "os" |
|
| 4 |
+ "path" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+const ( |
|
| 10 |
+ OpenShiftConfigPathEnvVar = "OPENSHIFTCONFIG" |
|
| 11 |
+ OpenShiftConfigFlagName = "config" |
|
| 12 |
+ OpenShiftConfigFileName = ".openshiftconfig" |
|
| 13 |
+ OpenShiftConfigHomeDir = ".config/openshift" |
|
| 14 |
+ OpenShiftConfigHomeFileName = ".config" |
|
| 15 |
+ OpenShiftConfigHomeDirFileName = OpenShiftConfigHomeDir + "/" + OpenShiftConfigHomeFileName |
|
| 16 |
+ |
|
| 17 |
+ KubeConfigPathEnvVar = clientcmd.RecommendedConfigPathEnvVar |
|
| 18 |
+ KubeConfigFileName = ".kubeconfig" |
|
| 19 |
+ KubeConfigHomeDir = ".kube" |
|
| 20 |
+) |
|
| 21 |
+ |
|
| 22 |
+// Set up the rules and priorities for loading config files. |
|
| 23 |
+func NewOpenShiftClientConfigLoadingRules() *clientcmd.ClientConfigLoadingRules {
|
|
| 24 |
+ return clientcmd.NewClientConfigLoadingRules(FullClientConfigFilePriority()) |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+// File priority loading rules for OpenShift. |
|
| 28 |
+// 1. OPENSHIFTCONFIG env var |
|
| 29 |
+// 2. .openshiftconfig file in current directory |
|
| 30 |
+// 3. ~/.config/openshift/.config file |
|
| 31 |
+func OpenShiftClientConfigFilePriority() []string {
|
|
| 32 |
+ return []string{
|
|
| 33 |
+ os.Getenv(OpenShiftConfigPathEnvVar), |
|
| 34 |
+ OpenShiftConfigFileName, |
|
| 35 |
+ path.Join(os.Getenv("HOME"), OpenShiftConfigHomeDirFileName),
|
|
| 36 |
+ } |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+// File priority loading rules for Kube. |
|
| 40 |
+// 1. KUBECONFIG env var |
|
| 41 |
+// 2. .kubeconfig file in current directory |
|
| 42 |
+// 3. ~/.kube/.kubeconfig file |
|
| 43 |
+func KubeClientConfigFilePriority() []string {
|
|
| 44 |
+ return []string{
|
|
| 45 |
+ os.Getenv(KubeConfigPathEnvVar), |
|
| 46 |
+ KubeConfigFileName, |
|
| 47 |
+ path.Join(os.Getenv("HOME"), KubeConfigHomeDir, KubeConfigFileName),
|
|
| 48 |
+ } |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+func FullClientConfigFilePriority() []string {
|
|
| 52 |
+ return append(OpenShiftClientConfigFilePriority(), KubeClientConfigFilePriority()...) |
|
| 53 |
+} |
| 0 | 54 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,157 @@ |
| 0 |
+package config |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "reflect" |
|
| 5 |
+ |
|
| 6 |
+ clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" |
|
| 7 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// MergeConfig takes a haystack to look for existing stanzas in (probably the merged config), a config object to modify (probably |
|
| 11 |
+// either the local or envvar config), and the new additions to merge in. It tries to find equivalents for the addition inside of the |
|
| 12 |
+// haystack and uses the mapping to avoid creating additional stanzas with duplicate information. It either locates or original |
|
| 13 |
+// stanzas or creates new ones for clusters and users. Then it uses the mapped names to build the correct contexts |
|
| 14 |
+func MergeConfig(haystack, toModify, addition clientcmdapi.Config) (*clientcmdapi.Config, error) {
|
|
| 15 |
+ ret := toModify |
|
| 16 |
+ |
|
| 17 |
+ requestedClusterNamesToActualClusterNames := map[string]string{}
|
|
| 18 |
+ existingClusterNames, err := getMapKeys(reflect.ValueOf(haystack.Clusters)) |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ return nil, err |
|
| 21 |
+ } |
|
| 22 |
+ for requestedKey, needle := range addition.Clusters {
|
|
| 23 |
+ if existingName := FindExistingClusterName(haystack, needle); len(existingName) > 0 {
|
|
| 24 |
+ requestedClusterNamesToActualClusterNames[requestedKey] = existingName |
|
| 25 |
+ continue |
|
| 26 |
+ } |
|
| 27 |
+ |
|
| 28 |
+ uniqueName := getUniqueName(requestedKey, existingClusterNames) |
|
| 29 |
+ requestedClusterNamesToActualClusterNames[requestedKey] = uniqueName |
|
| 30 |
+ ret.Clusters[uniqueName] = needle |
|
| 31 |
+ } |
|
| 32 |
+ |
|
| 33 |
+ requestedAuthInfoNamesToActualAuthInfoNames := map[string]string{}
|
|
| 34 |
+ existingAuthInfoNames, err := getMapKeys(reflect.ValueOf(haystack.AuthInfos)) |
|
| 35 |
+ if err != nil {
|
|
| 36 |
+ return nil, err |
|
| 37 |
+ } |
|
| 38 |
+ for requestedKey, needle := range addition.AuthInfos {
|
|
| 39 |
+ if existingName := FindExistingAuthInfoName(haystack, needle); len(existingName) > 0 {
|
|
| 40 |
+ requestedAuthInfoNamesToActualAuthInfoNames[requestedKey] = existingName |
|
| 41 |
+ continue |
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ uniqueName := getUniqueName(requestedKey, existingAuthInfoNames) |
|
| 45 |
+ requestedAuthInfoNamesToActualAuthInfoNames[requestedKey] = uniqueName |
|
| 46 |
+ ret.AuthInfos[uniqueName] = needle |
|
| 47 |
+ } |
|
| 48 |
+ |
|
| 49 |
+ requestedContextNamesToActualContextNames := map[string]string{}
|
|
| 50 |
+ existingContextNames, err := getMapKeys(reflect.ValueOf(haystack.Contexts)) |
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ return nil, err |
|
| 53 |
+ } |
|
| 54 |
+ for requestedKey, needle := range addition.Contexts {
|
|
| 55 |
+ exists := false |
|
| 56 |
+ |
|
| 57 |
+ actualContext := clientcmdapi.NewContext() |
|
| 58 |
+ actualContext.AuthInfo, exists = requestedAuthInfoNamesToActualAuthInfoNames[needle.AuthInfo] |
|
| 59 |
+ if !exists {
|
|
| 60 |
+ actualContext.AuthInfo = needle.AuthInfo |
|
| 61 |
+ } |
|
| 62 |
+ actualContext.Cluster, exists = requestedClusterNamesToActualClusterNames[needle.Cluster] |
|
| 63 |
+ if !exists {
|
|
| 64 |
+ actualContext.Cluster = needle.Cluster |
|
| 65 |
+ } |
|
| 66 |
+ actualContext.Namespace = needle.Namespace |
|
| 67 |
+ actualContext.Extensions = needle.Extensions |
|
| 68 |
+ |
|
| 69 |
+ if existingName := FindExistingContextName(haystack, *actualContext); len(existingName) > 0 {
|
|
| 70 |
+ // if this already exists, just move to the next, our job is done |
|
| 71 |
+ requestedContextNamesToActualContextNames[requestedKey] = existingName |
|
| 72 |
+ continue |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ uniqueName := getUniqueName(actualContext.Cluster+"-"+actualContext.AuthInfo, existingContextNames) |
|
| 76 |
+ requestedContextNamesToActualContextNames[requestedKey] = uniqueName |
|
| 77 |
+ ret.Contexts[uniqueName] = *actualContext |
|
| 78 |
+ } |
|
| 79 |
+ |
|
| 80 |
+ if len(addition.CurrentContext) > 0 {
|
|
| 81 |
+ if newCurrentContext, exists := requestedContextNamesToActualContextNames[addition.CurrentContext]; exists {
|
|
| 82 |
+ ret.CurrentContext = newCurrentContext |
|
| 83 |
+ } else {
|
|
| 84 |
+ ret.CurrentContext = addition.CurrentContext |
|
| 85 |
+ } |
|
| 86 |
+ } |
|
| 87 |
+ |
|
| 88 |
+ return &ret, nil |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+// FindExistingClusterName finds the nickname for the passed cluster config |
|
| 92 |
+func FindExistingClusterName(haystack clientcmdapi.Config, needle clientcmdapi.Cluster) string {
|
|
| 93 |
+ for key, cluster := range haystack.Clusters {
|
|
| 94 |
+ if reflect.DeepEqual(cluster, needle) {
|
|
| 95 |
+ return key |
|
| 96 |
+ } |
|
| 97 |
+ } |
|
| 98 |
+ |
|
| 99 |
+ return "" |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+// FindExistingAuthInfoName finds the nickname for the passed auth info |
|
| 103 |
+func FindExistingAuthInfoName(haystack clientcmdapi.Config, needle clientcmdapi.AuthInfo) string {
|
|
| 104 |
+ for key, authInfo := range haystack.AuthInfos {
|
|
| 105 |
+ if reflect.DeepEqual(authInfo, needle) {
|
|
| 106 |
+ return key |
|
| 107 |
+ } |
|
| 108 |
+ } |
|
| 109 |
+ |
|
| 110 |
+ return "" |
|
| 111 |
+} |
|
| 112 |
+ |
|
| 113 |
+// FindExistingContextName finds the nickname for the passed context |
|
| 114 |
+func FindExistingContextName(haystack clientcmdapi.Config, needle clientcmdapi.Context) string {
|
|
| 115 |
+ for key, context := range haystack.Contexts {
|
|
| 116 |
+ if reflect.DeepEqual(context, needle) {
|
|
| 117 |
+ return key |
|
| 118 |
+ } |
|
| 119 |
+ } |
|
| 120 |
+ |
|
| 121 |
+ return "" |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+func getMapKeys(theMap reflect.Value) (*util.StringSet, error) {
|
|
| 125 |
+ if theMap.Kind() != reflect.Map {
|
|
| 126 |
+ return nil, fmt.Errorf("theMap must be of type %v, not %v", reflect.Map, theMap.Kind())
|
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ ret := &util.StringSet{}
|
|
| 130 |
+ |
|
| 131 |
+ switch theMap.Kind() {
|
|
| 132 |
+ case reflect.Map: |
|
| 133 |
+ for _, keyValue := range theMap.MapKeys() {
|
|
| 134 |
+ ret.Insert(keyValue.String()) |
|
| 135 |
+ } |
|
| 136 |
+ |
|
| 137 |
+ } |
|
| 138 |
+ |
|
| 139 |
+ return ret, nil |
|
| 140 |
+ |
|
| 141 |
+} |
|
| 142 |
+ |
|
| 143 |
+func getUniqueName(basename string, existingNames *util.StringSet) string {
|
|
| 144 |
+ if !existingNames.Has(basename) {
|
|
| 145 |
+ return basename |
|
| 146 |
+ } |
|
| 147 |
+ |
|
| 148 |
+ for i := 0; i < 100; i++ {
|
|
| 149 |
+ trialName := fmt.Sprintf("%v-%d", basename, i)
|
|
| 150 |
+ if !existingNames.Has(trialName) {
|
|
| 151 |
+ return trialName |
|
| 152 |
+ } |
|
| 153 |
+ } |
|
| 154 |
+ |
|
| 155 |
+ return string(util.NewUUID()) |
|
| 156 |
+} |
| 0 | 157 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,159 @@ |
| 0 |
+package config |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io/ioutil" |
|
| 5 |
+ "os" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/golang/glog" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
| 10 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" |
|
| 11 |
+ clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" |
|
| 12 |
+ |
|
| 13 |
+ "github.com/openshift/origin/pkg/cmd/flagtypes" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+const ( |
|
| 17 |
+ fromKube = "fromkube" |
|
| 18 |
+ fromOpenShift = "fromopenshift" |
|
| 19 |
+) |
|
| 20 |
+ |
|
| 21 |
+// A ConfigStore is the representation of a config from one individual config file. Can be used |
|
| 22 |
+// to persist configs by being explicit about the file to save. |
|
| 23 |
+type ConfigStore struct {
|
|
| 24 |
+ Config *clientcmdapi.Config |
|
| 25 |
+ Path string |
|
| 26 |
+ providerEngine string |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+func (c *ConfigStore) FromOpenShift() bool {
|
|
| 30 |
+ return c.providerEngine == fromOpenShift |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+func (c *ConfigStore) FromKube() bool {
|
|
| 34 |
+ return c.providerEngine == fromKube |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+// Load a ConfigStore from the explicit path to a config file provided as argument |
|
| 38 |
+// Error if not found. |
|
| 39 |
+func LoadFrom(path string) (*ConfigStore, error) {
|
|
| 40 |
+ data, err := ioutil.ReadFile(path) |
|
| 41 |
+ if err == nil {
|
|
| 42 |
+ config, err := clientcmd.Load(data) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ return nil, err |
|
| 45 |
+ } |
|
| 46 |
+ return &ConfigStore{config, path, fromOpenShift}, nil
|
|
| 47 |
+ } |
|
| 48 |
+ |
|
| 49 |
+ return nil, fmt.Errorf("Unable to load config file from '%v': %v", path, err.Error())
|
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// Load a ConfigStore using the priority conventions declared by the ClientConfigLoadingRules. |
|
| 53 |
+// Error if none can be found. |
|
| 54 |
+func LoadWithLoadingRules() (store *ConfigStore, err error) {
|
|
| 55 |
+ loadingRules := map[string][]string{
|
|
| 56 |
+ fromOpenShift: OpenShiftClientConfigFilePriority(), |
|
| 57 |
+ fromKube: KubeClientConfigFilePriority(), |
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ for source, priorities := range loadingRules {
|
|
| 61 |
+ for _, path := range priorities {
|
|
| 62 |
+ data, err := ioutil.ReadFile(path) |
|
| 63 |
+ if err != nil && !os.IsNotExist(err) {
|
|
| 64 |
+ return nil, fmt.Errorf("Unable to load config file from %v: %v", path, err.Error())
|
|
| 65 |
+ } |
|
| 66 |
+ if err == nil {
|
|
| 67 |
+ config, err := clientcmd.Load(data) |
|
| 68 |
+ if err != nil {
|
|
| 69 |
+ return store, err |
|
| 70 |
+ } |
|
| 71 |
+ return &ConfigStore{config, path, source}, nil
|
|
| 72 |
+ } |
|
| 73 |
+ } |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ return nil, fmt.Errorf("Unable to load a config file from any of the expected locations.")
|
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// Create a new file to store configs and returns the ConfigStore that represents it. |
|
| 80 |
+func CreateEmpty() (*ConfigStore, error) {
|
|
| 81 |
+ configPathToCreateIfNotFound := fmt.Sprintf("%v/%v", os.Getenv("HOME"), OpenShiftConfigHomeDirFileName)
|
|
| 82 |
+ |
|
| 83 |
+ glog.V(3).Infof("A new config will be created at: %v ", configPathToCreateIfNotFound)
|
|
| 84 |
+ |
|
| 85 |
+ newConfig := clientcmdapi.NewConfig() |
|
| 86 |
+ |
|
| 87 |
+ if err := os.MkdirAll(fmt.Sprintf("%v/%v", os.Getenv("HOME"), OpenShiftConfigHomeDir), 0755); err != nil {
|
|
| 88 |
+ return nil, fmt.Errorf("Tried to create a new config file but failed while creating directory %v: %v", OpenShiftConfigHomeDirFileName, err)
|
|
| 89 |
+ } |
|
| 90 |
+ glog.V(5).Infof("Created directory %v", "~/"+OpenShiftConfigHomeDir)
|
|
| 91 |
+ |
|
| 92 |
+ if err := clientcmd.WriteToFile(*newConfig, configPathToCreateIfNotFound); err != nil {
|
|
| 93 |
+ return nil, fmt.Errorf("Tried to create a new config file but failed with: %v", err)
|
|
| 94 |
+ } |
|
| 95 |
+ glog.V(5).Infof("Created file %v", configPathToCreateIfNotFound)
|
|
| 96 |
+ |
|
| 97 |
+ data, err := ioutil.ReadFile(configPathToCreateIfNotFound) |
|
| 98 |
+ if err != nil {
|
|
| 99 |
+ return nil, err |
|
| 100 |
+ } |
|
| 101 |
+ |
|
| 102 |
+ config, err := clientcmd.Load(data) |
|
| 103 |
+ if err != nil {
|
|
| 104 |
+ return nil, err |
|
| 105 |
+ } |
|
| 106 |
+ |
|
| 107 |
+ return &ConfigStore{config, configPathToCreateIfNotFound, fromOpenShift}, nil
|
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+// Save the provided config attributes to this ConfigStore. |
|
| 111 |
+func (c *ConfigStore) SaveToFile(credentialsName string, namespace string, clientCfg *client.Config, rawCfg clientcmdapi.Config) error {
|
|
| 112 |
+ glog.V(4).Infof("Trying to merge and update %v config to '%v'...", c.providerEngine, c.Path)
|
|
| 113 |
+ |
|
| 114 |
+ config := clientcmdapi.NewConfig() |
|
| 115 |
+ |
|
| 116 |
+ credentials := clientcmdapi.NewAuthInfo() |
|
| 117 |
+ credentials.Token = clientCfg.BearerToken |
|
| 118 |
+ credentials.ClientCertificate = clientCfg.TLSClientConfig.CertFile |
|
| 119 |
+ credentials.ClientCertificateData = clientCfg.TLSClientConfig.CertData |
|
| 120 |
+ credentials.ClientKey = clientCfg.TLSClientConfig.KeyFile |
|
| 121 |
+ credentials.ClientKeyData = clientCfg.TLSClientConfig.KeyData |
|
| 122 |
+ if len(credentialsName) == 0 {
|
|
| 123 |
+ credentialsName = "osc-login" |
|
| 124 |
+ } |
|
| 125 |
+ config.AuthInfos[credentialsName] = *credentials |
|
| 126 |
+ |
|
| 127 |
+ serverAddr := flagtypes.Addr{Value: clientCfg.Host}.Default()
|
|
| 128 |
+ clusterName := fmt.Sprintf("%v:%v", serverAddr.Host, serverAddr.Port)
|
|
| 129 |
+ cluster := clientcmdapi.NewCluster() |
|
| 130 |
+ cluster.Server = clientCfg.Host |
|
| 131 |
+ cluster.CertificateAuthority = clientCfg.CAFile |
|
| 132 |
+ cluster.CertificateAuthorityData = clientCfg.CAData |
|
| 133 |
+ cluster.InsecureSkipTLSVerify = clientCfg.Insecure |
|
| 134 |
+ config.Clusters[clusterName] = *cluster |
|
| 135 |
+ |
|
| 136 |
+ contextName := clusterName + "-" + credentialsName |
|
| 137 |
+ context := clientcmdapi.NewContext() |
|
| 138 |
+ context.Cluster = clusterName |
|
| 139 |
+ context.AuthInfo = credentialsName |
|
| 140 |
+ context.Namespace = namespace |
|
| 141 |
+ config.Contexts[contextName] = *context |
|
| 142 |
+ config.CurrentContext = contextName |
|
| 143 |
+ |
|
| 144 |
+ configToModify := c.Config |
|
| 145 |
+ |
|
| 146 |
+ configToWrite, err := MergeConfig(rawCfg, *configToModify, *config) |
|
| 147 |
+ if err != nil {
|
|
| 148 |
+ return err |
|
| 149 |
+ } |
|
| 150 |
+ |
|
| 151 |
+ // TODO need to handle file not writable (probably create a copy) |
|
| 152 |
+ err = clientcmd.WriteToFile(*configToWrite, c.Path) |
|
| 153 |
+ if err != nil {
|
|
| 154 |
+ return err |
|
| 155 |
+ } |
|
| 156 |
+ |
|
| 157 |
+ return nil |
|
| 158 |
+} |
| 0 | 159 |
deleted file mode 100644 |
| ... | ... |
@@ -1,178 +0,0 @@ |
| 1 |
-package login |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "os" |
|
| 6 |
- |
|
| 7 |
- "github.com/golang/glog" |
|
| 8 |
- "github.com/spf13/cobra" |
|
| 9 |
- |
|
| 10 |
- kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
| 11 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" |
|
| 12 |
- clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" |
|
| 13 |
- kcmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" |
|
| 14 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 15 |
- |
|
| 16 |
- "github.com/openshift/origin/pkg/client" |
|
| 17 |
- "github.com/openshift/origin/pkg/cmd/flagtypes" |
|
| 18 |
- osclientcmd "github.com/openshift/origin/pkg/cmd/util/clientcmd" |
|
| 19 |
- "github.com/openshift/origin/pkg/cmd/util/tokencmd" |
|
| 20 |
-) |
|
| 21 |
- |
|
| 22 |
-func NewCmdLogin(f *osclientcmd.Factory, parentName, name string) *cobra.Command {
|
|
| 23 |
- cmds := &cobra.Command{
|
|
| 24 |
- Use: name, |
|
| 25 |
- Short: "Logs in and returns a session token", |
|
| 26 |
- Long: `Logs in to the OpenShift server and prints out a session token. |
|
| 27 |
- |
|
| 28 |
-Username and password can be provided through flags, the command will |
|
| 29 |
-prompt for user input if not provided. |
|
| 30 |
-`, |
|
| 31 |
- Run: func(cmd *cobra.Command, args []string) {
|
|
| 32 |
- clientCfg, err := f.OpenShiftClientConfig.ClientConfig() |
|
| 33 |
- if err != nil {
|
|
| 34 |
- glog.Fatalf("%v\n", err)
|
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- username := "" |
|
| 38 |
- |
|
| 39 |
- // check to see if we're already signed in. If so, simply make sure that .kubeconfig has that information |
|
| 40 |
- if userFullName, err := whoami(clientCfg); err == nil {
|
|
| 41 |
- if err := updateKubeconfigFile(userFullName, clientCfg.BearerToken, f.OpenShiftClientConfig); err != nil {
|
|
| 42 |
- glog.Fatalf("%v\n", err)
|
|
| 43 |
- } |
|
| 44 |
- username = userFullName |
|
| 45 |
- |
|
| 46 |
- } else {
|
|
| 47 |
- usernameFlag := kcmdutil.GetFlagString(cmd, "username") |
|
| 48 |
- passwordFlag := kcmdutil.GetFlagString(cmd, "password") |
|
| 49 |
- |
|
| 50 |
- accessToken, err := tokencmd.RequestToken(clientCfg, os.Stdin, usernameFlag, passwordFlag) |
|
| 51 |
- if err != nil {
|
|
| 52 |
- glog.Fatalf("%v\n", err)
|
|
| 53 |
- } |
|
| 54 |
- |
|
| 55 |
- clientCfg.BearerToken = accessToken |
|
| 56 |
- |
|
| 57 |
- if userFullName, err := whoami(clientCfg); err == nil {
|
|
| 58 |
- err = updateKubeconfigFile(userFullName, accessToken, f.OpenShiftClientConfig) |
|
| 59 |
- if err != nil {
|
|
| 60 |
- glog.Fatalf("%v\n", err)
|
|
| 61 |
- } else {
|
|
| 62 |
- username = userFullName |
|
| 63 |
- } |
|
| 64 |
- } else {
|
|
| 65 |
- glog.Fatalf("%v\n", err)
|
|
| 66 |
- } |
|
| 67 |
- } |
|
| 68 |
- |
|
| 69 |
- fmt.Printf("Logged into %v as %v\n", clientCfg.Host, username)
|
|
| 70 |
- }, |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- cmds.Flags().StringP("username", "u", "", "Username, will prompt if not provided")
|
|
| 74 |
- cmds.Flags().StringP("password", "p", "", "Password, will prompt if not provided")
|
|
| 75 |
- return cmds |
|
| 76 |
-} |
|
| 77 |
- |
|
| 78 |
-func whoami(clientCfg *kclient.Config) (string, error) {
|
|
| 79 |
- osClient, err := client.New(clientCfg) |
|
| 80 |
- if err != nil {
|
|
| 81 |
- return "", err |
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- me, err := osClient.Users().Get("~")
|
|
| 85 |
- if err != nil {
|
|
| 86 |
- return "", err |
|
| 87 |
- } |
|
| 88 |
- |
|
| 89 |
- return me.FullName, nil |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 |
-func updateKubeconfigFile(username, token string, clientCfg clientcmd.ClientConfig) error {
|
|
| 93 |
- rawMergedConfig, err := clientCfg.RawConfig() |
|
| 94 |
- if err != nil {
|
|
| 95 |
- return err |
|
| 96 |
- } |
|
| 97 |
- clientConfig, err := clientCfg.ClientConfig() |
|
| 98 |
- if err != nil {
|
|
| 99 |
- return err |
|
| 100 |
- } |
|
| 101 |
- namespace, err := clientCfg.Namespace() |
|
| 102 |
- if err != nil {
|
|
| 103 |
- return err |
|
| 104 |
- } |
|
| 105 |
- |
|
| 106 |
- config := clientcmdapi.NewConfig() |
|
| 107 |
- |
|
| 108 |
- credentialsName := username |
|
| 109 |
- if len(credentialsName) == 0 {
|
|
| 110 |
- credentialsName = "osc-login" |
|
| 111 |
- } |
|
| 112 |
- credentials := clientcmdapi.NewAuthInfo() |
|
| 113 |
- credentials.Token = token |
|
| 114 |
- config.AuthInfos[credentialsName] = *credentials |
|
| 115 |
- |
|
| 116 |
- serverAddr := flagtypes.Addr{Value: clientConfig.Host}.Default()
|
|
| 117 |
- clusterName := fmt.Sprintf("%v:%v", serverAddr.Host, serverAddr.Port)
|
|
| 118 |
- cluster := clientcmdapi.NewCluster() |
|
| 119 |
- cluster.Server = clientConfig.Host |
|
| 120 |
- cluster.InsecureSkipTLSVerify = clientConfig.Insecure |
|
| 121 |
- cluster.CertificateAuthority = clientConfig.CAFile |
|
| 122 |
- config.Clusters[clusterName] = *cluster |
|
| 123 |
- |
|
| 124 |
- contextName := clusterName + "-" + credentialsName |
|
| 125 |
- context := clientcmdapi.NewContext() |
|
| 126 |
- context.Cluster = clusterName |
|
| 127 |
- context.AuthInfo = credentialsName |
|
| 128 |
- context.Namespace = namespace |
|
| 129 |
- config.Contexts[contextName] = *context |
|
| 130 |
- |
|
| 131 |
- config.CurrentContext = contextName |
|
| 132 |
- |
|
| 133 |
- configToModify, err := getConfigFromFile(".kubeconfig")
|
|
| 134 |
- if err != nil {
|
|
| 135 |
- return err |
|
| 136 |
- } |
|
| 137 |
- |
|
| 138 |
- configToWrite, err := MergeConfig(rawMergedConfig, *configToModify, *config) |
|
| 139 |
- if err != nil {
|
|
| 140 |
- return err |
|
| 141 |
- } |
|
| 142 |
- err = clientcmd.WriteToFile(*configToWrite, ".kubeconfig") |
|
| 143 |
- if err != nil {
|
|
| 144 |
- return err |
|
| 145 |
- } |
|
| 146 |
- |
|
| 147 |
- return nil |
|
| 148 |
- |
|
| 149 |
-} |
|
| 150 |
- |
|
| 151 |
-func getConfigFromFile(filename string) (*clientcmdapi.Config, error) {
|
|
| 152 |
- var err error |
|
| 153 |
- config, err := clientcmd.LoadFromFile(filename) |
|
| 154 |
- if err != nil && !os.IsNotExist(err) {
|
|
| 155 |
- return nil, err |
|
| 156 |
- } |
|
| 157 |
- |
|
| 158 |
- if config == nil {
|
|
| 159 |
- config = clientcmdapi.NewConfig() |
|
| 160 |
- } |
|
| 161 |
- |
|
| 162 |
- return config, nil |
|
| 163 |
-} |
|
| 164 |
- |
|
| 165 |
-func getUniqueName(basename string, existingNames *util.StringSet) string {
|
|
| 166 |
- if !existingNames.Has(basename) {
|
|
| 167 |
- return basename |
|
| 168 |
- } |
|
| 169 |
- |
|
| 170 |
- for i := 0; i < 100; i++ {
|
|
| 171 |
- trialName := fmt.Sprintf("%v-%d", basename, i)
|
|
| 172 |
- if !existingNames.Has(trialName) {
|
|
| 173 |
- return trialName |
|
| 174 |
- } |
|
| 175 |
- } |
|
| 176 |
- |
|
| 177 |
- return string(util.NewUUID()) |
|
| 178 |
-} |
| 179 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,142 +0,0 @@ |
| 1 |
-package login |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "reflect" |
|
| 6 |
- |
|
| 7 |
- clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" |
|
| 8 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-// MergeConfig takes a haystack to look for existing stanzas in (probably the merged config), a config object to modify (probably |
|
| 12 |
-// either the local or envvar config), and the new additions to merge in. It tries to find equivalents for the addition inside of the |
|
| 13 |
-// haystack and uses the mapping to avoid creating additional stanzas with duplicate information. It either locates or original |
|
| 14 |
-// stanzas or creates new ones for clusters and users. Then it uses the mapped names to build the correct contexts |
|
| 15 |
-func MergeConfig(haystack, toModify, addition clientcmdapi.Config) (*clientcmdapi.Config, error) {
|
|
| 16 |
- ret := toModify |
|
| 17 |
- |
|
| 18 |
- requestedClusterNamesToActualClusterNames := map[string]string{}
|
|
| 19 |
- existingClusterNames, err := getMapKeys(reflect.ValueOf(haystack.Clusters)) |
|
| 20 |
- if err != nil {
|
|
| 21 |
- return nil, err |
|
| 22 |
- } |
|
| 23 |
- for requestedKey, needle := range addition.Clusters {
|
|
| 24 |
- if existingName := FindExistingClusterName(haystack, needle); len(existingName) > 0 {
|
|
| 25 |
- requestedClusterNamesToActualClusterNames[requestedKey] = existingName |
|
| 26 |
- continue |
|
| 27 |
- } |
|
| 28 |
- |
|
| 29 |
- uniqueName := getUniqueName(requestedKey, existingClusterNames) |
|
| 30 |
- requestedClusterNamesToActualClusterNames[requestedKey] = uniqueName |
|
| 31 |
- ret.Clusters[uniqueName] = needle |
|
| 32 |
- } |
|
| 33 |
- |
|
| 34 |
- requestedAuthInfoNamesToActualAuthInfoNames := map[string]string{}
|
|
| 35 |
- existingAuthInfoNames, err := getMapKeys(reflect.ValueOf(haystack.AuthInfos)) |
|
| 36 |
- if err != nil {
|
|
| 37 |
- return nil, err |
|
| 38 |
- } |
|
| 39 |
- for requestedKey, needle := range addition.AuthInfos {
|
|
| 40 |
- if existingName := FindExistingAuthInfoName(haystack, needle); len(existingName) > 0 {
|
|
| 41 |
- requestedAuthInfoNamesToActualAuthInfoNames[requestedKey] = existingName |
|
| 42 |
- continue |
|
| 43 |
- } |
|
| 44 |
- |
|
| 45 |
- uniqueName := getUniqueName(requestedKey, existingAuthInfoNames) |
|
| 46 |
- requestedAuthInfoNamesToActualAuthInfoNames[requestedKey] = uniqueName |
|
| 47 |
- ret.AuthInfos[uniqueName] = needle |
|
| 48 |
- } |
|
| 49 |
- |
|
| 50 |
- requestedContextNamesToActualContextNames := map[string]string{}
|
|
| 51 |
- existingContextNames, err := getMapKeys(reflect.ValueOf(haystack.Contexts)) |
|
| 52 |
- if err != nil {
|
|
| 53 |
- return nil, err |
|
| 54 |
- } |
|
| 55 |
- for requestedKey, needle := range addition.Contexts {
|
|
| 56 |
- exists := false |
|
| 57 |
- |
|
| 58 |
- actualContext := clientcmdapi.NewContext() |
|
| 59 |
- actualContext.AuthInfo, exists = requestedAuthInfoNamesToActualAuthInfoNames[needle.AuthInfo] |
|
| 60 |
- if !exists {
|
|
| 61 |
- actualContext.AuthInfo = needle.AuthInfo |
|
| 62 |
- } |
|
| 63 |
- actualContext.Cluster, exists = requestedClusterNamesToActualClusterNames[needle.Cluster] |
|
| 64 |
- if !exists {
|
|
| 65 |
- actualContext.Cluster = needle.Cluster |
|
| 66 |
- } |
|
| 67 |
- actualContext.Namespace = needle.Namespace |
|
| 68 |
- actualContext.Extensions = needle.Extensions |
|
| 69 |
- |
|
| 70 |
- if existingName := FindExistingContextName(haystack, *actualContext); len(existingName) > 0 {
|
|
| 71 |
- // if this already exists, just move to the next, our job is done |
|
| 72 |
- requestedContextNamesToActualContextNames[requestedKey] = existingName |
|
| 73 |
- continue |
|
| 74 |
- } |
|
| 75 |
- |
|
| 76 |
- uniqueName := getUniqueName(actualContext.Cluster+"-"+actualContext.AuthInfo, existingContextNames) |
|
| 77 |
- requestedContextNamesToActualContextNames[requestedKey] = uniqueName |
|
| 78 |
- ret.Contexts[uniqueName] = *actualContext |
|
| 79 |
- } |
|
| 80 |
- |
|
| 81 |
- if len(addition.CurrentContext) > 0 {
|
|
| 82 |
- if newCurrentContext, exists := requestedContextNamesToActualContextNames[addition.CurrentContext]; exists {
|
|
| 83 |
- ret.CurrentContext = newCurrentContext |
|
| 84 |
- } else {
|
|
| 85 |
- ret.CurrentContext = addition.CurrentContext |
|
| 86 |
- } |
|
| 87 |
- } |
|
| 88 |
- |
|
| 89 |
- return &ret, nil |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 |
-// FindExistingClusterName finds the nickname for the passed cluster config |
|
| 93 |
-func FindExistingClusterName(haystack clientcmdapi.Config, needle clientcmdapi.Cluster) string {
|
|
| 94 |
- for key, cluster := range haystack.Clusters {
|
|
| 95 |
- if reflect.DeepEqual(cluster, needle) {
|
|
| 96 |
- return key |
|
| 97 |
- } |
|
| 98 |
- } |
|
| 99 |
- |
|
| 100 |
- return "" |
|
| 101 |
-} |
|
| 102 |
- |
|
| 103 |
-// FindExistingAuthInfoName finds the nickname for the passed auth info |
|
| 104 |
-func FindExistingAuthInfoName(haystack clientcmdapi.Config, needle clientcmdapi.AuthInfo) string {
|
|
| 105 |
- for key, authInfo := range haystack.AuthInfos {
|
|
| 106 |
- if reflect.DeepEqual(authInfo, needle) {
|
|
| 107 |
- return key |
|
| 108 |
- } |
|
| 109 |
- } |
|
| 110 |
- |
|
| 111 |
- return "" |
|
| 112 |
-} |
|
| 113 |
- |
|
| 114 |
-// FindExistingContextName finds the nickname for the passed context |
|
| 115 |
-func FindExistingContextName(haystack clientcmdapi.Config, needle clientcmdapi.Context) string {
|
|
| 116 |
- for key, context := range haystack.Contexts {
|
|
| 117 |
- if reflect.DeepEqual(context, needle) {
|
|
| 118 |
- return key |
|
| 119 |
- } |
|
| 120 |
- } |
|
| 121 |
- |
|
| 122 |
- return "" |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-func getMapKeys(theMap reflect.Value) (*util.StringSet, error) {
|
|
| 126 |
- if theMap.Kind() != reflect.Map {
|
|
| 127 |
- return nil, fmt.Errorf("theMap must be of type %v, not %v", reflect.Map, theMap.Kind())
|
|
| 128 |
- } |
|
| 129 |
- |
|
| 130 |
- ret := &util.StringSet{}
|
|
| 131 |
- |
|
| 132 |
- switch theMap.Kind() {
|
|
| 133 |
- case reflect.Map: |
|
| 134 |
- for _, keyValue := range theMap.MapKeys() {
|
|
| 135 |
- ret.Insert(keyValue.String()) |
|
| 136 |
- } |
|
| 137 |
- |
|
| 138 |
- } |
|
| 139 |
- |
|
| 140 |
- return ret, nil |
|
| 141 |
- |
|
| 142 |
-} |
| ... | ... |
@@ -157,7 +157,8 @@ func NewCmdRegistry(f *clientcmd.Factory, parentName, name string, out io.Writer |
| 157 | 157 |
if len(cfg.Credentials) == 0 {
|
| 158 | 158 |
glog.Fatalf("You must specify a .kubeconfig file path containing credentials for connecting the registry to the master with --credentials")
|
| 159 | 159 |
} |
| 160 |
- credentials, err := (&kclientcmd.ClientConfigLoadingRules{CommandLinePath: cfg.Credentials}).Load()
|
|
| 160 |
+ clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{cfg.Credentials, []string{}}
|
|
| 161 |
+ credentials, err := clientConfigLoadingRules.Load() |
|
| 161 | 162 |
if err != nil {
|
| 162 | 163 |
glog.Fatalf("The provided credentials %q could not be loaded: %v", cfg.Credentials, err)
|
| 163 | 164 |
} |
| ... | ... |
@@ -21,7 +21,6 @@ import ( |
| 21 | 21 |
configcmd "github.com/openshift/origin/pkg/config/cmd" |
| 22 | 22 |
dapi "github.com/openshift/origin/pkg/deploy/api" |
| 23 | 23 |
"github.com/openshift/origin/pkg/generate/app" |
| 24 |
- //imageapi "github.com/openshift/origin/pkg/image/api" |
|
| 25 | 24 |
) |
| 26 | 25 |
|
| 27 | 26 |
const longDesc = ` |
| ... | ... |
@@ -148,7 +147,9 @@ func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out io.Writer) |
| 148 | 148 |
if len(cfg.Credentials) == 0 {
|
| 149 | 149 |
glog.Fatalf("You must specify a .kubeconfig file path containing credentials for connecting the router to the master with --credentials")
|
| 150 | 150 |
} |
| 151 |
- credentials, err := (&kclientcmd.ClientConfigLoadingRules{CommandLinePath: cfg.Credentials}).Load()
|
|
| 151 |
+ |
|
| 152 |
+ clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{cfg.Credentials, []string{}}
|
|
| 153 |
+ credentials, err := clientConfigLoadingRules.Load() |
|
| 152 | 154 |
if err != nil {
|
| 153 | 155 |
glog.Fatalf("The provided credentials %q could not be loaded: %v", cfg.Credentials, err)
|
| 154 | 156 |
} |
| ... | ... |
@@ -9,7 +9,6 @@ import ( |
| 9 | 9 |
"github.com/openshift/origin/pkg/cmd/cli" |
| 10 | 10 |
"github.com/openshift/origin/pkg/cmd/experimental/config" |
| 11 | 11 |
"github.com/openshift/origin/pkg/cmd/experimental/generate" |
| 12 |
- "github.com/openshift/origin/pkg/cmd/experimental/login" |
|
| 13 | 12 |
"github.com/openshift/origin/pkg/cmd/experimental/policy" |
| 14 | 13 |
"github.com/openshift/origin/pkg/cmd/experimental/project" |
| 15 | 14 |
exregistry "github.com/openshift/origin/pkg/cmd/experimental/registry" |
| ... | ... |
@@ -122,7 +121,6 @@ func newExperimentalCommand(parentName, name string) *cobra.Command {
|
| 122 | 122 |
experimental.AddCommand(tokens.NewCmdTokens(f, subName, "tokens")) |
| 123 | 123 |
experimental.AddCommand(policy.NewCommandPolicy(f, subName, "policy")) |
| 124 | 124 |
experimental.AddCommand(generate.NewCmdGenerate(f, subName, "generate")) |
| 125 |
- experimental.AddCommand(login.NewCmdLogin(f, subName, "login")) |
|
| 126 | 125 |
experimental.AddCommand(exrouter.NewCmdRouter(f, subName, "router", os.Stdout)) |
| 127 | 126 |
experimental.AddCommand(exregistry.NewCmdRegistry(f, subName, "registry", os.Stdout)) |
| 128 | 127 |
return experimental |
| ... | ... |
@@ -14,7 +14,6 @@ import ( |
| 14 | 14 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record" |
| 15 | 15 |
"github.com/coreos/go-systemd/daemon" |
| 16 | 16 |
"github.com/golang/glog" |
| 17 |
- |
|
| 18 | 17 |
"github.com/openshift/origin/pkg/cmd/server/etcd" |
| 19 | 18 |
"github.com/openshift/origin/pkg/cmd/server/kubernetes" |
| 20 | 19 |
"github.com/openshift/origin/pkg/cmd/server/origin" |
| 21 | 20 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,29 @@ |
| 0 |
+package clientcmd |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net/http" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/golang/glog" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+const ( |
|
| 9 |
+ unauthorizedErrorMessage = `Your session has expired. Use the following command to log in again: |
|
| 10 |
+ osc login` |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+type statusHandlerClient struct {
|
|
| 14 |
+ delegate *http.Client |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+func (client *statusHandlerClient) Do(req *http.Request) (*http.Response, error) {
|
|
| 18 |
+ resp, err := client.delegate.Do(req) |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ return nil, err |
|
| 21 |
+ } |
|
| 22 |
+ |
|
| 23 |
+ if resp.StatusCode == http.StatusUnauthorized {
|
|
| 24 |
+ glog.Fatal(unauthorizedErrorMessage) |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ return resp, err |
|
| 28 |
+} |
| 0 | 29 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,54 @@ |
| 0 |
+package clientcmd |
|
| 1 |
+ |
|
| 2 |
+import "strings" |
|
| 3 |
+ |
|
| 4 |
+const ( |
|
| 5 |
+ unknownReason = 0 |
|
| 6 |
+ noServerFoundReason = 1 |
|
| 7 |
+ certificateAuthorityUnknownReason = 2 |
|
| 8 |
+ |
|
| 9 |
+ certificateAuthorityUnknownMsg = "The server uses a certificate signed by unknown authority. You may need to use the --certificate-authority flag to provide the path to a certificate file for the certificate authority, or --insecure-skip-tls-verify to bypass the certificate check and use insecure connections." |
|
| 10 |
+ notConfiguredMsg = `OpenShift is not configured. You need to run the login command in order to create a default config for your server and credentials: |
|
| 11 |
+ osc login |
|
| 12 |
+You can also run this command again providing the path to a config file directly, either through the --config flag of the OPENSHIFTCONFIG environment variable. |
|
| 13 |
+` |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+func GetPrettyMessageFor(err error) string {
|
|
| 17 |
+ if err == nil {
|
|
| 18 |
+ return "" |
|
| 19 |
+ } |
|
| 20 |
+ |
|
| 21 |
+ reason := detectReason(err) |
|
| 22 |
+ |
|
| 23 |
+ switch reason {
|
|
| 24 |
+ case noServerFoundReason: |
|
| 25 |
+ return notConfiguredMsg |
|
| 26 |
+ |
|
| 27 |
+ case certificateAuthorityUnknownReason: |
|
| 28 |
+ return certificateAuthorityUnknownMsg |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ return err.Error() |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func IsNoServerFound(err error) bool {
|
|
| 35 |
+ return detectReason(err) == noServerFoundReason |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func IsCertificateAuthorityUnknown(err error) bool {
|
|
| 39 |
+ return detectReason(err) == certificateAuthorityUnknownReason |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+func detectReason(err error) int {
|
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ switch {
|
|
| 45 |
+ case strings.Contains(err.Error(), "certificate signed by unknown authority"): |
|
| 46 |
+ return certificateAuthorityUnknownReason |
|
| 47 |
+ |
|
| 48 |
+ case strings.Contains(err.Error(), "no server found for"): |
|
| 49 |
+ return noServerFoundReason |
|
| 50 |
+ } |
|
| 51 |
+ } |
|
| 52 |
+ return unknownReason |
|
| 53 |
+} |
| ... | ... |
@@ -2,7 +2,7 @@ package clientcmd |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
- "os" |
|
| 5 |
+ "net/http" |
|
| 6 | 6 |
|
| 7 | 7 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
| 8 | 8 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" |
| ... | ... |
@@ -17,34 +17,39 @@ import ( |
| 17 | 17 |
|
| 18 | 18 |
"github.com/openshift/origin/pkg/api/latest" |
| 19 | 19 |
"github.com/openshift/origin/pkg/client" |
| 20 |
+ "github.com/openshift/origin/pkg/cmd/cli/config" |
|
| 20 | 21 |
"github.com/openshift/origin/pkg/cmd/cli/describe" |
| 21 | 22 |
) |
| 22 | 23 |
|
| 23 |
-const defaultClusterURL = "https://localhost:8443" |
|
| 24 |
- |
|
| 25 | 24 |
// NewFactory creates a default Factory for commands that should share identical server |
| 26 | 25 |
// connection behavior. Most commands should use this method to get a factory. |
| 27 | 26 |
func New(flags *pflag.FlagSet) *Factory {
|
| 28 |
- // Override global default to https and port 8443 |
|
| 29 |
- clientcmd.DefaultCluster.Server = defaultClusterURL |
|
| 27 |
+ // Override global default to "" so we force the client to ask for user input |
|
| 28 |
+ // TODO refactor this usptream: |
|
| 29 |
+ // DefaultCluster should not be a global |
|
| 30 |
+ // A call to ClientConfig() should always return the best clientCfg possible |
|
| 31 |
+ // even if an error was returned, and let the caller decide what to do |
|
| 32 |
+ clientcmd.DefaultCluster.Server = "" |
|
| 30 | 33 |
|
| 31 | 34 |
// TODO: there should be two client configs, one for OpenShift, and one for Kubernetes |
| 32 | 35 |
clientConfig := DefaultClientConfig(flags) |
| 33 | 36 |
f := NewFactory(clientConfig) |
| 34 | 37 |
f.BindFlags(flags) |
| 38 |
+ |
|
| 35 | 39 |
return f |
| 36 | 40 |
} |
| 37 | 41 |
|
| 38 | 42 |
// Copy of kubectl/cmd/DefaultClientConfig, using NewNonInteractiveDeferredLoadingClientConfig |
| 39 | 43 |
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
|
| 40 |
- loadingRules := clientcmd.NewClientConfigLoadingRules() |
|
| 41 |
- loadingRules.EnvVarPath = os.Getenv(clientcmd.RecommendedConfigPathEnvVar) |
|
| 42 |
- flags.StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") |
|
| 44 |
+ loadingRules := config.NewOpenShiftClientConfigLoadingRules() |
|
| 45 |
+ |
|
| 46 |
+ flags.StringVar(&loadingRules.CommandLinePath, config.OpenShiftConfigFlagName, "", "Path to the config file to use for CLI requests.") |
|
| 43 | 47 |
|
| 44 | 48 |
overrides := &clientcmd.ConfigOverrides{}
|
| 45 | 49 |
overrideFlags := clientcmd.RecommendedConfigOverrideFlags("")
|
| 46 | 50 |
overrideFlags.ContextOverrideFlags.NamespaceShort = "n" |
| 47 | 51 |
clientcmd.BindOverrideFlags(overrides, flags, overrideFlags) |
| 52 |
+ |
|
| 48 | 53 |
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) |
| 49 | 54 |
|
| 50 | 55 |
return clientConfig |
| ... | ... |
@@ -66,70 +71,79 @@ func NewFactory(clientConfig clientcmd.ClientConfig) *Factory {
|
| 66 | 66 |
return mapper, api.Scheme |
| 67 | 67 |
} |
| 68 | 68 |
|
| 69 |
- // Save original RESTClient function |
|
| 70 |
- kRESTClientFunc := w.Factory.RESTClient |
|
| 71 | 69 |
w.RESTClient = func(cmd *cobra.Command, mapping *meta.RESTMapping) (resource.RESTClient, error) {
|
| 70 |
+ oClient, kClient, err := w.Clients(cmd) |
|
| 71 |
+ if err != nil {
|
|
| 72 |
+ return nil, fmt.Errorf("unable to create client %s: %v", mapping.Kind, err)
|
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 72 | 75 |
if latest.OriginKind(mapping.Kind, mapping.APIVersion) {
|
| 73 |
- cfg, err := w.OpenShiftClientConfig.ClientConfig() |
|
| 74 |
- if err != nil {
|
|
| 75 |
- return nil, fmt.Errorf("unable to find client config %s: %v", mapping.Kind, err)
|
|
| 76 |
- } |
|
| 77 |
- cli, err := client.New(cfg) |
|
| 78 |
- if err != nil {
|
|
| 79 |
- return nil, fmt.Errorf("unable to create client %s: %v", mapping.Kind, err)
|
|
| 80 |
- } |
|
| 81 |
- return cli.RESTClient, nil |
|
| 76 |
+ return oClient.RESTClient, nil |
|
| 77 |
+ } else {
|
|
| 78 |
+ return kClient.RESTClient, nil |
|
| 82 | 79 |
} |
| 83 |
- return kRESTClientFunc(cmd, mapping) |
|
| 84 | 80 |
} |
| 85 | 81 |
|
| 86 |
- // Save original Describer function |
|
| 87 |
- kDescriberFunc := w.Factory.Describer |
|
| 88 | 82 |
w.Describer = func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Describer, error) {
|
| 83 |
+ oClient, kClient, err := w.Clients(cmd) |
|
| 84 |
+ if err != nil {
|
|
| 85 |
+ return nil, fmt.Errorf("unable to create client %s: %v", mapping.Kind, err)
|
|
| 86 |
+ } |
|
| 87 |
+ |
|
| 88 |
+ cfg, err := w.OpenShiftClientConfig.ClientConfig() |
|
| 89 |
+ if err != nil {
|
|
| 90 |
+ return nil, fmt.Errorf("unable to describe %s: %v", mapping.Kind, err)
|
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 89 | 93 |
if latest.OriginKind(mapping.Kind, mapping.APIVersion) {
|
| 90 |
- cfg, err := w.OpenShiftClientConfig.ClientConfig() |
|
| 91 |
- if err != nil {
|
|
| 92 |
- return nil, fmt.Errorf("unable to describe %s: %v", mapping.Kind, err)
|
|
| 93 |
- } |
|
| 94 |
- cli, err := client.New(cfg) |
|
| 95 |
- if err != nil {
|
|
| 96 |
- return nil, fmt.Errorf("unable to describe %s: %v", mapping.Kind, err)
|
|
| 97 |
- } |
|
| 98 |
- kubeClient, err := kclient.New(cfg) |
|
| 99 |
- if err != nil {
|
|
| 100 |
- return nil, fmt.Errorf("unable to describe %s: %v", mapping.Kind, err)
|
|
| 101 |
- } |
|
| 102 |
- describer, ok := describe.DescriberFor(mapping.Kind, cli, kubeClient, cfg.Host) |
|
| 94 |
+ describer, ok := describe.DescriberFor(mapping.Kind, oClient, kClient, cfg.Host) |
|
| 103 | 95 |
if !ok {
|
| 104 | 96 |
return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
|
| 105 | 97 |
} |
| 106 | 98 |
return describer, nil |
| 107 | 99 |
} |
| 108 |
- return kDescriberFunc(cmd, mapping) |
|
| 100 |
+ return w.Factory.Describer(cmd, mapping) |
|
| 109 | 101 |
} |
| 110 | 102 |
|
| 111 | 103 |
w.Printer = func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
|
| 112 | 104 |
return describe.NewHumanReadablePrinter(noHeaders), nil |
| 113 | 105 |
} |
| 114 | 106 |
|
| 107 |
+ w.DefaultNamespace = func(cmd *cobra.Command) (string, error) {
|
|
| 108 |
+ return w.OpenShiftClientConfig.Namespace() |
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 115 | 111 |
return w |
| 116 | 112 |
} |
| 117 | 113 |
|
| 118 | 114 |
// Clients returns an OpenShift and Kubernetes client. |
| 119 | 115 |
func (f *Factory) Clients(cmd *cobra.Command) (*client.Client, *kclient.Client, error) {
|
| 120 |
- os, err := f.OpenShiftClientConfig.ClientConfig() |
|
| 116 |
+ cfg, err := f.OpenShiftClientConfig.ClientConfig() |
|
| 117 |
+ if err != nil {
|
|
| 118 |
+ return nil, nil, err |
|
| 119 |
+ } |
|
| 120 |
+ |
|
| 121 |
+ transport, err := kclient.TransportFor(cfg) |
|
| 121 | 122 |
if err != nil {
|
| 122 | 123 |
return nil, nil, err |
| 123 | 124 |
} |
| 124 |
- oc, err := client.New(os) |
|
| 125 |
+ httpClient := &http.Client{
|
|
| 126 |
+ Transport: transport, |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ oClient, err := client.New(cfg) |
|
| 125 | 130 |
if err != nil {
|
| 126 | 131 |
return nil, nil, err |
| 127 | 132 |
} |
| 128 |
- kc, err := f.Client(cmd) |
|
| 133 |
+ kClient, err := kclient.New(cfg) |
|
| 129 | 134 |
if err != nil {
|
| 130 | 135 |
return nil, nil, err |
| 131 | 136 |
} |
| 132 |
- return oc, kc, nil |
|
| 137 |
+ |
|
| 138 |
+ oClient.Client = &statusHandlerClient{httpClient}
|
|
| 139 |
+ kClient.Client = &statusHandlerClient{httpClient}
|
|
| 140 |
+ |
|
| 141 |
+ return oClient, kClient, nil |
|
| 133 | 142 |
} |
| 134 | 143 |
|
| 135 | 144 |
// ShortcutExpander is a RESTMapper that can be used for OpenShift resources. |
| ... | ... |
@@ -1,18 +1,16 @@ |
| 1 | 1 |
package util |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "os" |
|
| 5 |
- |
|
| 6 | 4 |
"github.com/spf13/pflag" |
| 7 | 5 |
|
| 8 | 6 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" |
| 7 |
+ "github.com/openshift/origin/pkg/cmd/cli/config" |
|
| 9 | 8 |
) |
| 10 | 9 |
|
| 11 |
-// Copy of kubectl/cmd/DefaultClientConfig, using NewNonInteractiveDeferredLoadingClientConfig |
|
| 12 | 10 |
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
|
| 13 |
- loadingRules := clientcmd.NewClientConfigLoadingRules() |
|
| 14 |
- loadingRules.EnvVarPath = os.Getenv(clientcmd.RecommendedConfigPathEnvVar) |
|
| 15 |
- flags.StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") |
|
| 11 |
+ loadingRules := config.NewOpenShiftClientConfigLoadingRules() |
|
| 12 |
+ |
|
| 13 |
+ flags.StringVar(&loadingRules.CommandLinePath, config.OpenShiftConfigFlagName, "", "Path to the config file to use for CLI requests.") |
|
| 16 | 14 |
|
| 17 | 15 |
overrides := &clientcmd.ConfigOverrides{}
|
| 18 | 16 |
overrideFlags := clientcmd.RecommendedConfigOverrideFlags("")
|
| ... | ... |
@@ -1,20 +1,23 @@ |
| 1 | 1 |
package util |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "bufio" |
|
| 4 | 5 |
"fmt" |
| 5 | 6 |
"io" |
| 6 | 7 |
"os" |
| 8 |
+ "strings" |
|
| 7 | 9 |
|
| 8 | 10 |
"github.com/docker/docker/pkg/term" |
| 9 | 11 |
"github.com/golang/glog" |
| 10 | 12 |
) |
| 11 | 13 |
|
| 14 |
+// Takes an io.Reader and prompt for user input if it's a terminal, returning the result. |
|
| 12 | 15 |
func PromptForString(r io.Reader, format string, a ...interface{}) string {
|
| 13 | 16 |
fmt.Printf(format, a...) |
| 14 | 17 |
return readInput(r) |
| 15 | 18 |
} |
| 16 | 19 |
|
| 17 |
-// TODO not tested on other platforms |
|
| 20 |
+// Prompt for user input by disabling echo in terminal, useful for password prompt. |
|
| 18 | 21 |
func PromptForPasswordString(r io.Reader, format string, a ...interface{}) string {
|
| 19 | 22 |
if file, ok := r.(*os.File); ok {
|
| 20 | 23 |
inFd := file.Fd() |
| ... | ... |
@@ -37,18 +40,50 @@ func PromptForPasswordString(r io.Reader, format string, a ...interface{}) strin
|
| 37 | 37 |
fmt.Printf("\n")
|
| 38 | 38 |
|
| 39 | 39 |
return input |
| 40 |
- } else {
|
|
| 41 |
- glog.V(3).Infof("Stdin is not a terminal")
|
|
| 42 |
- return PromptForString(r, format, a...) |
|
| 43 | 40 |
} |
| 44 |
- } else {
|
|
| 45 |
- glog.V(3).Infof("Unable to use a TTY")
|
|
| 41 |
+ glog.V(3).Infof("Stdin is not a terminal")
|
|
| 46 | 42 |
return PromptForString(r, format, a...) |
| 47 | 43 |
} |
| 44 |
+ return PromptForString(r, format, a...) |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+// Prompt for user input of a boolean value. The accepted values are: |
|
| 48 |
+// yes, y, true, t, 1 (not case sensitive) |
|
| 49 |
+// no, n, false, f, 0 (not case sensitive) |
|
| 50 |
+// A valid answer is mandatory so it will keep asking until an answer is provided. |
|
| 51 |
+func PromptForBool(r io.Reader, format string, a ...interface{}) bool {
|
|
| 52 |
+ str := PromptForString(r, format, a...) |
|
| 53 |
+ switch strings.ToLower(str) {
|
|
| 54 |
+ case "1", "t", "true", "y", "yes": |
|
| 55 |
+ return true |
|
| 56 |
+ case "0", "f", "false", "n", "no": |
|
| 57 |
+ return false |
|
| 58 |
+ } |
|
| 59 |
+ fmt.Println("Please enter 'yes' or 'no'.")
|
|
| 60 |
+ return PromptForBool(r, format, a...) |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+// Prompt for user input but take a default in case nothing is provided. |
|
| 64 |
+func PromptForStringWithDefault(r io.Reader, def string, format string, a ...interface{}) string {
|
|
| 65 |
+ s := PromptForString(r, format, a...) |
|
| 66 |
+ if len(s) == 0 {
|
|
| 67 |
+ return def |
|
| 68 |
+ } |
|
| 69 |
+ return s |
|
| 48 | 70 |
} |
| 49 | 71 |
|
| 50 | 72 |
func readInput(r io.Reader) string {
|
| 73 |
+ if IsTerminal(r) {
|
|
| 74 |
+ reader := bufio.NewReader(r) |
|
| 75 |
+ result, _ := reader.ReadString('\n')
|
|
| 76 |
+ return strings.TrimSuffix(result, "\n") |
|
| 77 |
+ } |
|
| 51 | 78 |
var result string |
| 52 | 79 |
fmt.Fscan(r, &result) |
| 53 | 80 |
return result |
| 54 | 81 |
} |
| 82 |
+ |
|
| 83 |
+func IsTerminal(r io.Reader) bool {
|
|
| 84 |
+ file, ok := r.(*os.File) |
|
| 85 |
+ return ok && term.IsTerminal(file.Fd()) |
|
| 86 |
+} |
| ... | ... |
@@ -40,7 +40,7 @@ func (client *challengingClient) Do(req *http.Request) (*http.Response, error) {
|
| 40 | 40 |
missingUsername := len(username) == 0 |
| 41 | 41 |
missingPassword := len(password) == 0 |
| 42 | 42 |
|
| 43 |
- if (missingUsername || missingPassword) && (client.reader != nil) {
|
|
| 43 |
+ if (missingUsername || missingPassword) && client.reader != nil {
|
|
| 44 | 44 |
fmt.Printf("Authenticate for \"%v\"\n", realm)
|
| 45 | 45 |
if missingUsername {
|
| 46 | 46 |
username = util.PromptForString(client.reader, "Username: ") |
| ... | ... |
@@ -7,10 +7,10 @@ import ( |
| 7 | 7 |
"regexp" |
| 8 | 8 |
|
| 9 | 9 |
kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
| 10 |
- "github.com/golang/glog" |
|
| 11 |
- |
|
| 12 |
- "github.com/openshift/origin/pkg/auth/server/tokenrequest" |
|
| 13 | 10 |
"github.com/openshift/origin/pkg/client" |
| 11 |
+ "github.com/openshift/origin/pkg/oauth/server/osinserver" |
|
| 12 |
+ |
|
| 13 |
+ server "github.com/openshift/origin/pkg/cmd/server/origin" |
|
| 14 | 14 |
) |
| 15 | 15 |
|
| 16 | 16 |
const accessTokenRedirectPattern = `#access_token=([\w]+)&` |
| ... | ... |
@@ -45,15 +45,10 @@ func RequestToken(clientCfg *kclient.Config, reader io.Reader, defaultUsername s |
| 45 | 45 |
|
| 46 | 46 |
osClient.Client = &challengingClient{httpClient, reader, defaultUsername, defaultPassword}
|
| 47 | 47 |
|
| 48 |
- result := osClient.Get().AbsPath("oauth", "authorize").Param("response_type", "token").Param("client_id", "openshift-challenging-client").Do()
|
|
| 48 |
+ result := osClient.Get().AbsPath(server.OpenShiftOAuthAPIPrefix, osinserver.AuthorizePath).Param("response_type", "token").Param("client_id", "openshift-challenging-client").Do()
|
|
| 49 | 49 |
|
| 50 | 50 |
if len(tokenGetter.accessToken) == 0 {
|
| 51 |
- if result.Error() != nil {
|
|
| 52 |
- glog.Errorf("Error making server request: %v", result.Error())
|
|
| 53 |
- } |
|
| 54 |
- |
|
| 55 |
- requestTokenURL := clientCfg.Host + "/oauth" /* clean up after auth.go dies */ + tokenrequest.RequestTokenEndpoint |
|
| 56 |
- return "", errors.New("Unable to get token. Try visiting " + requestTokenURL + " for a new token.")
|
|
| 51 |
+ return "", result.Error() |
|
| 57 | 52 |
} |
| 58 | 53 |
|
| 59 | 54 |
return tokenGetter.accessToken, nil |
| 60 | 55 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,147 @@ |
| 0 |
+// +build integration,!no-etcd |
|
| 1 |
+ |
|
| 2 |
+package integration |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "os" |
|
| 6 |
+ "testing" |
|
| 7 |
+ |
|
| 8 |
+ kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
| 9 |
+ "github.com/openshift/origin/pkg/client" |
|
| 10 |
+ "github.com/openshift/origin/pkg/cmd/cli/cmd" |
|
| 11 |
+ newproject "github.com/openshift/origin/pkg/cmd/experimental/project" |
|
| 12 |
+ "github.com/openshift/origin/pkg/cmd/util/clientcmd" |
|
| 13 |
+ "github.com/openshift/origin/pkg/user/api" |
|
| 14 |
+ "github.com/spf13/pflag" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+func init() {
|
|
| 18 |
+ requireEtcd() |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func TestLogin(t *testing.T) {
|
|
| 22 |
+ startConfig, err := StartTestMaster() |
|
| 23 |
+ if err != nil {
|
|
| 24 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ openshiftClient, openshiftClientConfig, err := startConfig.GetOpenshiftClient() |
|
| 28 |
+ if err != nil {
|
|
| 29 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ // empty config, should display message |
|
| 33 |
+ loginOptions := newLoginOptions("", "", "", "", false)
|
|
| 34 |
+ err = loginOptions.GatherServerInfo() |
|
| 35 |
+ if err == nil {
|
|
| 36 |
+ t.Errorf("Raw login should error out")
|
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ username := "joe" |
|
| 40 |
+ password := "pass" |
|
| 41 |
+ project := "the-singularity-is-near" |
|
| 42 |
+ server := openshiftClientConfig.Host |
|
| 43 |
+ |
|
| 44 |
+ loginOptions = newLoginOptions(server, username, password, "", true) |
|
| 45 |
+ |
|
| 46 |
+ if err = loginOptions.GatherServerInfo(); err != nil {
|
|
| 47 |
+ t.Fatalf("Error trying to determine server info: ", err)
|
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ if err = loginOptions.GatherAuthInfo(); err != nil {
|
|
| 51 |
+ t.Fatalf("Error trying to determine auth info: ", err)
|
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ me, err := loginOptions.Whoami() |
|
| 55 |
+ if err != nil {
|
|
| 56 |
+ t.Errorf("unexpected error: ", err)
|
|
| 57 |
+ } |
|
| 58 |
+ if me.Name != "anypassword:"+username {
|
|
| 59 |
+ t.Fatalf("Unexpected user after authentication: %v", me.Name)
|
|
| 60 |
+ } |
|
| 61 |
+ |
|
| 62 |
+ newProjectOptions := &newproject.NewProjectOptions{
|
|
| 63 |
+ Client: openshiftClient, |
|
| 64 |
+ ProjectName: project, |
|
| 65 |
+ AdminRole: "admin", |
|
| 66 |
+ MasterPolicyNamespace: "master", |
|
| 67 |
+ AdminUser: "anypassword:" + username, |
|
| 68 |
+ } |
|
| 69 |
+ if err := newProjectOptions.Run(); err != nil {
|
|
| 70 |
+ t.Fatalf("unexpected error, a project is required to continue: ", err)
|
|
| 71 |
+ } |
|
| 72 |
+ |
|
| 73 |
+ oClient, _ := client.New(loginOptions.Config) |
|
| 74 |
+ p, err := oClient.Projects().Get(project) |
|
| 75 |
+ if err != nil {
|
|
| 76 |
+ t.Errorf("unexpected error: ", err)
|
|
| 77 |
+ } |
|
| 78 |
+ |
|
| 79 |
+ if p.Name != project {
|
|
| 80 |
+ t.Fatalf("Got the unexpected project: %v", p.Name)
|
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ // TODO Commented because of incorrectly hitting cache when listing projects. |
|
| 84 |
+ // Should be enabled again when cache eviction is properly fixed. |
|
| 85 |
+ |
|
| 86 |
+ // err = loginOptions.GatherProjectInfo() |
|
| 87 |
+ // if err != nil {
|
|
| 88 |
+ // t.Fatalf("unexpected error: ", err)
|
|
| 89 |
+ // } |
|
| 90 |
+ |
|
| 91 |
+ // if loginOptions.Project != project {
|
|
| 92 |
+ // t.Fatalf("Expected project %v but got %v", project, loginOptions.Project)
|
|
| 93 |
+ // } |
|
| 94 |
+ |
|
| 95 |
+ // configFile, err := ioutil.TempFile("", "openshiftconfig")
|
|
| 96 |
+ // if err != nil {
|
|
| 97 |
+ // t.Fatalf("unexpected error: %v", err)
|
|
| 98 |
+ // } |
|
| 99 |
+ // defer os.Remove(configFile.Name()) |
|
| 100 |
+ |
|
| 101 |
+ // if _, err = loginOptions.SaveConfig(configFile.Name()); err != nil {
|
|
| 102 |
+ // t.Fatalf("unexpected error: ", err)
|
|
| 103 |
+ // } |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+func newLoginOptions(server string, username string, password string, context string, insecure bool) *cmd.LoginOptions {
|
|
| 107 |
+ flagset := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
|
|
| 108 |
+ factory := clientcmd.New(flagset) |
|
| 109 |
+ |
|
| 110 |
+ flags := []string{}
|
|
| 111 |
+ |
|
| 112 |
+ if len(server) > 0 {
|
|
| 113 |
+ flags = append(flags, "--server="+server) |
|
| 114 |
+ } |
|
| 115 |
+ if len(context) > 0 {
|
|
| 116 |
+ flags = append(flags, "--context="+context) |
|
| 117 |
+ } |
|
| 118 |
+ if insecure {
|
|
| 119 |
+ flags = append(flags, "--insecure-skip-tls-verify") |
|
| 120 |
+ } |
|
| 121 |
+ |
|
| 122 |
+ flagset.Parse(flags) |
|
| 123 |
+ |
|
| 124 |
+ loginOptions := &cmd.LoginOptions{
|
|
| 125 |
+ ClientConfig: factory.OpenShiftClientConfig, |
|
| 126 |
+ Reader: os.Stdin, |
|
| 127 |
+ Username: username, |
|
| 128 |
+ Password: password, |
|
| 129 |
+ } |
|
| 130 |
+ |
|
| 131 |
+ return loginOptions |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+func whoami(clientCfg *kclient.Config) (*api.User, error) {
|
|
| 135 |
+ oClient, err := client.New(clientCfg) |
|
| 136 |
+ if err != nil {
|
|
| 137 |
+ return nil, err |
|
| 138 |
+ } |
|
| 139 |
+ |
|
| 140 |
+ me, err := oClient.Users().Get("~")
|
|
| 141 |
+ if err != nil {
|
|
| 142 |
+ return nil, err |
|
| 143 |
+ } |
|
| 144 |
+ |
|
| 145 |
+ return me, nil |
|
| 146 |
+} |