Browse code

Adding source secret

jhadvig authored on 2015/08/27 00:58:58
Showing 15 changed files
... ...
@@ -802,6 +802,25 @@ Create a new secret based on a key file or on files within a directory
802 802
 ====
803 803
 
804 804
 
805
+== oc secrets new-basicauth
806
+Create a new secret for basic authentication
807
+
808
+====
809
+
810
+[options="nowrap"]
811
+----
812
+  // If your basic authentication method requires only username and password or token, add it by using:
813
+  $ openshift cli secrets new-basicauth SECRET --username=USERNAME --password=PASSWORD
814
+
815
+  // If your basic authentication method requires also CA certificate, add it by using:
816
+  $ openshift cli secrets new-basicauth SECRET --username=USERNAME --password=PASSWORD --ca-cert=FILENAME
817
+
818
+  // If you do already have a .gitconfig file needed for authentication, you can create a gitconfig secret by using:
819
+  $ openshift cli secrets new SECRET path/to/.gitconfig
820
+----
821
+====
822
+
823
+
805 824
 == oc secrets new-dockercfg
806 825
 Create a new dockercfg secret
807 826
 
... ...
@@ -810,10 +829,10 @@ Create a new dockercfg secret
810 810
 [options="nowrap"]
811 811
 ----
812 812
   // If you don't already have a .dockercfg file, you can create a dockercfg secret directly by using:
813
-  $ openshift cli secrets new-dockercfg SECRET_NAME --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
813
+  $ openshift cli secrets new-dockercfg SECRET --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
814 814
 
815 815
   // If you do already have a .dockercfg file, you can create a dockercfg secret by using:
816
-  $ openshift cli secrets new SECRET_NAME path/to/.dockercfg
816
+  $ openshift cli secrets new SECRET path/to/.dockercfg
817 817
 
818 818
   // To add new secret to 'imagePullSecrets' for the node, or 'secrets' for builds, use:
819 819
   $ openshift cli edit SERVICE_ACCOUNT
... ...
@@ -821,6 +840,25 @@ Create a new dockercfg secret
821 821
 ====
822 822
 
823 823
 
824
+== oc secrets new-sshauth
825
+Create a new secret for SSH authentication
826
+
827
+====
828
+
829
+[options="nowrap"]
830
+----
831
+  // If your SSH authentication method requires only private SSH key, add it by using:
832
+  $ openshift cli secrets new-sshauth SECRET --ssh-privatekey=FILENAME
833
+
834
+  // If your SSH authentication method requires also CA certificate, add it by using:
835
+  $ openshift cli secrets new-sshauth SECRET --ssh-privatekey=FILENAME --ca-cert=FILENAME
836
+
837
+  // If you do already have a .gitconfig file needed for authentication, you can create a gitconfig secret by using:
838
+  $ openshift cli secrets new SECRET path/to/.gitconfig
839
+----
840
+====
841
+
842
+
824 843
 == oc start-build
825 844
 Starts a new build
826 845
 
... ...
@@ -481,7 +481,24 @@ function cleanup_openshift {
481 481
 
482 482
 	echo "[INFO] Cleanup complete"
483 483
 	set -e
484
-}	
484
+}
485
+
486
+# create a .gitconfig for test-cmd secrets
487
+function create_gitconfig {
488
+	USERNAME=sample-user
489
+	PASSWORD=password
490
+	GITCONFIG_DIR=$(mktemp -d /tmp/test-gitconfig.XXXX)
491
+	touch ${GITCONFIG_DIR}/.gitconfig
492
+	git config --file ${GITCONFIG_DIR}/.gitconfig user.name ${USERNAME}
493
+	git config --file ${GITCONFIG_DIR}/.gitconfig user.token ${PASSWORD}
494
+	echo ${GITCONFIG_DIR}/.gitconfig
495
+}
496
+
497
+function create_valid_file {
498
+	FILE_DIR=$(mktemp -d /tmp/test-file.XXXX)
499
+	touch ${FILE_DIR}/${1}
500
+	echo ${FILE_DIR}/${1}
501
+}
485 502
 
486 503
 # install the router for the extended tests
487 504
 function install_router {
... ...
@@ -129,7 +129,7 @@ func NewCommandCLI(name, fullName string, out io.Writer) *cobra.Command {
129 129
 				cmd.NewCmdRun(fullName, f, in, out, errout),
130 130
 				cmd.NewCmdAttach(fullName, f, in, out, errout),
131 131
 				policy.NewCmdPolicy(policy.PolicyRecommendedName, fullName+" "+policy.PolicyRecommendedName, f, out),
132
-				secrets.NewCmdSecrets(secrets.SecretsRecommendedName, fullName+" "+secrets.SecretsRecommendedName, f, out, fullName+" edit"),
132
+				secrets.NewCmdSecrets(secrets.SecretsRecommendedName, fullName+" "+secrets.SecretsRecommendedName, f, in, out, fullName+" edit"),
133 133
 			},
134 134
 		},
135 135
 		{
136 136
new file mode 100644
... ...
@@ -0,0 +1,215 @@
0
+package secrets
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io"
6
+	"io/ioutil"
7
+
8
+	"k8s.io/kubernetes/pkg/api"
9
+	"k8s.io/kubernetes/pkg/client"
10
+	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
11
+
12
+	cmdutil "github.com/openshift/origin/pkg/cmd/util"
13
+
14
+	"github.com/spf13/cobra"
15
+)
16
+
17
+const (
18
+	// CreateBasicAuthSecretRecommendedCommandName represents name of subcommand for `oc secrets` command
19
+	CreateBasicAuthSecretRecommendedCommandName = "new-basicauth"
20
+
21
+	createBasicAuthSecretLong = `
22
+Create a new basic authentication secret
23
+
24
+Basic authenticate secrets are used to authenticate against SCM servers.
25
+
26
+When creating applications, you may have a SCM server that requires basic authentication - username, password.
27
+In order for the nodes to clone source code on your behalf, they have to have the credentials. You can provide 
28
+this information by creating a 'basicauth' secret and attaching it to your service account.`
29
+
30
+	createBasicAuthSecretExample = `  // If your basic authentication method requires only username and password or token, add it by using:
31
+  $ %[1]s SECRET --username=USERNAME --password=PASSWORD
32
+
33
+  // If your basic authentication method requires also CA certificate, add it by using:
34
+  $ %[1]s SECRET --username=USERNAME --password=PASSWORD --ca-cert=FILENAME
35
+
36
+  // If you do already have a .gitconfig file needed for authentication, you can create a gitconfig secret by using:
37
+  $ %[2]s SECRET path/to/.gitconfig`
38
+)
39
+
40
+// CreateBasicAuthSecretOptions holds the credential needed to authenticate against SCM servers.
41
+type CreateBasicAuthSecretOptions struct {
42
+	SecretName      string
43
+	Username        string
44
+	Password        string
45
+	CertificatePath string
46
+	GitConfigPath   string
47
+
48
+	PromptForPassword bool
49
+
50
+	Reader io.Reader
51
+	Out    io.Writer
52
+
53
+	SecretsInterface client.SecretsInterface
54
+}
55
+
56
+// NewCmdCreateBasicAuthSecret implements the OpenShift cli secrets new-basicauth subcommand
57
+func NewCmdCreateBasicAuthSecret(name, fullName string, f *kcmdutil.Factory, reader io.Reader, out io.Writer, newSecretFullName, ocEditFullName string) *cobra.Command {
58
+	o := &CreateBasicAuthSecretOptions{
59
+		Out:    out,
60
+		Reader: reader,
61
+	}
62
+
63
+	cmd := &cobra.Command{
64
+		Use:     fmt.Sprintf("%s SECRET_NAME --username=USERNAME --password=PASSWORD [--ca-cert=FILENAME --gitconfig=FILENAME]", name),
65
+		Short:   "Create a new secret for basic authentication",
66
+		Long:    createBasicAuthSecretLong,
67
+		Example: fmt.Sprintf(createBasicAuthSecretExample, fullName, newSecretFullName, ocEditFullName),
68
+		Run: func(c *cobra.Command, args []string) {
69
+			if err := o.Complete(f, args); err != nil {
70
+				kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
71
+			}
72
+
73
+			if err := o.Validate(); err != nil {
74
+				kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
75
+			}
76
+
77
+			if len(kcmdutil.GetFlagString(c, "output")) != 0 {
78
+				secret, err := o.NewBasicAuthSecret()
79
+				kcmdutil.CheckErr(err)
80
+
81
+				kcmdutil.CheckErr(f.PrintObject(c, secret, out))
82
+				return
83
+			}
84
+
85
+			if err := o.CreateBasicAuthSecret(); err != nil {
86
+				kcmdutil.CheckErr(err)
87
+			}
88
+		},
89
+	}
90
+
91
+	cmd.Flags().StringVar(&o.Username, "username", "", "Username for Git authentication")
92
+	cmd.Flags().StringVar(&o.Password, "password", "", "Password or token for Git authentication")
93
+	cmd.Flags().StringVar(&o.CertificatePath, "ca-cert", "", "Path to a certificate file")
94
+	cmd.Flags().StringVar(&o.GitConfigPath, "gitconfig", "", "Path to a .gitconfig file")
95
+	cmd.Flags().BoolVarP(&o.PromptForPassword, "prompt", "", false, "Prompt for password or token")
96
+
97
+	// autocompletion hints
98
+	cmd.MarkFlagFilename("ca-cert")
99
+	cmd.MarkFlagFilename("gitconfig")
100
+
101
+	kcmdutil.AddPrinterFlags(cmd)
102
+
103
+	return cmd
104
+}
105
+
106
+// CreateBasicAuthSecret saves created Secret structure and prints the secret name to the output on success.
107
+func (o *CreateBasicAuthSecretOptions) CreateBasicAuthSecret() error {
108
+	secret, err := o.NewBasicAuthSecret()
109
+	if err != nil {
110
+		return err
111
+	}
112
+
113
+	if _, err := o.SecretsInterface.Create(secret); err != nil {
114
+		return err
115
+	}
116
+
117
+	fmt.Fprintf(o.GetOut(), "secret/%s\n", secret.Name)
118
+	return nil
119
+}
120
+
121
+// NewBasicAuthSecret builds up the Secret structure containing secret name, type and data structure
122
+// containing desired credentials.
123
+func (o *CreateBasicAuthSecretOptions) NewBasicAuthSecret() (*api.Secret, error) {
124
+	secret := &api.Secret{}
125
+	secret.Name = o.SecretName
126
+	secret.Type = api.SecretTypeOpaque
127
+	secret.Data = map[string][]byte{}
128
+
129
+	if len(o.Username) != 0 {
130
+		secret.Data[SourceUsername] = []byte(o.Username)
131
+	}
132
+
133
+	if len(o.Password) != 0 {
134
+		secret.Data[SourcePassword] = []byte(o.Password)
135
+	}
136
+
137
+	if len(o.CertificatePath) != 0 {
138
+		caContent, err := ioutil.ReadFile(o.CertificatePath)
139
+		if err != nil {
140
+			return nil, err
141
+		}
142
+		secret.Data[SourceCertificate] = caContent
143
+	}
144
+
145
+	if len(o.GitConfigPath) != 0 {
146
+		gitConfig, err := ioutil.ReadFile(o.GitConfigPath)
147
+		if err != nil {
148
+			return nil, err
149
+		}
150
+		secret.Data[SourceGitConfig] = gitConfig
151
+	}
152
+
153
+	return secret, nil
154
+}
155
+
156
+// Complete fills CreateBasicAuthSecretOptions fields with data and checks for mutual exclusivity
157
+// between flags from different option groups.
158
+func (o *CreateBasicAuthSecretOptions) Complete(f *kcmdutil.Factory, args []string) error {
159
+	if len(args) != 1 {
160
+		return errors.New("must have exactly one argument: secret name")
161
+	}
162
+	o.SecretName = args[0]
163
+
164
+	if f != nil {
165
+		client, err := f.Client()
166
+		if err != nil {
167
+			return err
168
+		}
169
+		namespace, _, err := f.DefaultNamespace()
170
+		if err != nil {
171
+			return err
172
+		}
173
+		o.SecretsInterface = client.Secrets(namespace)
174
+	}
175
+
176
+	return nil
177
+}
178
+
179
+// Validate check if all necessary fields from CreateBasicAuthSecretOptions are present.
180
+func (o CreateBasicAuthSecretOptions) Validate() error {
181
+	if len(o.SecretName) == 0 {
182
+		return errors.New("basic authentication secret name must be present")
183
+	}
184
+
185
+	if o.PromptForPassword {
186
+		if len(o.Password) != 0 {
187
+			return errors.New("must provide either --prompt or --password flag")
188
+		}
189
+		if cmdutil.IsTerminal(o.Reader) {
190
+			o.Password = cmdutil.PromptForPasswordString(o.Reader, o.Out, "Password: ")
191
+			if len(o.Password) == 0 {
192
+				return errors.New("password must be provided")
193
+			}
194
+		} else {
195
+			return errors.New("provided reader is not a terminal")
196
+		}
197
+	} else {
198
+		if len(o.Username) == 0 && len(o.Password) == 0 && len(o.CertificatePath) == 0 {
199
+			return errors.New("must provide basic authentication credentials")
200
+		}
201
+	}
202
+
203
+	return nil
204
+}
205
+
206
+// GetOut check if the CreateBasicAuthSecretOptions Out Writer is set. Returns it if the Writer
207
+// is present, if not returns Writer on which all Write calls succeed without doing anything.
208
+func (o CreateBasicAuthSecretOptions) GetOut() io.Writer {
209
+	if o.Out == nil {
210
+		return ioutil.Discard
211
+	}
212
+
213
+	return o.Out
214
+}
0 215
new file mode 100644
... ...
@@ -0,0 +1,78 @@
0
+package secrets
1
+
2
+import (
3
+	"testing"
4
+)
5
+
6
+func TestValidateBasicAuth(t *testing.T) {
7
+	tests := []struct {
8
+		testName string
9
+		args     []string
10
+		params   CreateBasicAuthSecretOptions
11
+		expErr   bool
12
+	}{
13
+		{
14
+			testName: "validArgs",
15
+			args:     []string{"testSecret"},
16
+			params: CreateBasicAuthSecretOptions{
17
+				Username: "testUser",
18
+				Password: "testPassword",
19
+			},
20
+			expErr: false,
21
+		},
22
+		{
23
+			testName: "validArgsWithCertificate",
24
+			args:     []string{"testSecret"},
25
+			params: CreateBasicAuthSecretOptions{
26
+				Username:        "testUser",
27
+				Password:        "testPassword",
28
+				CertificatePath: "./bsFixtures/valid/ca.crt",
29
+			},
30
+			expErr: false,
31
+		},
32
+		{
33
+			testName: "validArgsWithGitconfig",
34
+			args:     []string{"testSecret"},
35
+			params: CreateBasicAuthSecretOptions{
36
+				Username:      "testUser",
37
+				Password:      "testPassword",
38
+				GitConfigPath: "./bsFixtures/leadingdot/.gitconfig",
39
+			},
40
+			expErr: false,
41
+		},
42
+		{
43
+			testName: "noName",
44
+			args:     []string{},
45
+			params: CreateBasicAuthSecretOptions{
46
+				Username: "testUser",
47
+				Password: "testPassword",
48
+			},
49
+			expErr: true, //"Must have exactly one argument: secret name"
50
+		},
51
+		{
52
+			testName: "noParams",
53
+			args:     []string{"testSecret"},
54
+			params:   CreateBasicAuthSecretOptions{},
55
+			expErr:   true, //"Must provide basic authentication credentials"
56
+		},
57
+		{
58
+			testName: "passwordAndPrompt",
59
+			args:     []string{"testSecret"},
60
+			params: CreateBasicAuthSecretOptions{
61
+				Username:          "testUser",
62
+				Password:          "testPassword",
63
+				PromptForPassword: true,
64
+			},
65
+			expErr: true, //"Must provide either --prompt or --password flag"
66
+		},
67
+	}
68
+
69
+	for _, test := range tests {
70
+		options := test.params
71
+		options.Complete(nil, test.args)
72
+		err := options.Validate()
73
+		if err != nil && !test.expErr {
74
+			t.Errorf("%s: unexpected error: %v", test.testName, err)
75
+		}
76
+	}
77
+}
0 78
new file mode 100644
1 79
new file mode 100644
2 80
new file mode 100644
... ...
@@ -34,17 +34,16 @@ nodes to pull images on your behalf, they have to have the credentials.  You can
34 34
 by creating a dockercfg secret and attaching it to your service account.`
35 35
 
36 36
 	createDockercfgExample = `  // If you don't already have a .dockercfg file, you can create a dockercfg secret directly by using:
37
-  $ %[1]s SECRET_NAME --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
37
+  $ %[1]s SECRET --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
38 38
 
39 39
   // If you do already have a .dockercfg file, you can create a dockercfg secret by using:
40
-  $ %[2]s SECRET_NAME path/to/.dockercfg
40
+  $ %[2]s SECRET path/to/.dockercfg
41 41
 
42 42
   // To add new secret to 'imagePullSecrets' for the node, or 'secrets' for builds, use:
43 43
   $ %[3]s SERVICE_ACCOUNT`
44 44
 )
45 45
 
46 46
 type CreateDockerConfigOptions struct {
47
-	SecretNamespace  string
48 47
 	SecretName       string
49 48
 	RegistryLocation string
50 49
 	Username         string
... ...
@@ -75,7 +74,7 @@ func NewCmdCreateDockerConfigSecret(name, fullName string, f *cmdutil.Factory, o
75 75
 			}
76 76
 
77 77
 			if len(cmdutil.GetFlagString(c, "output")) != 0 {
78
-				secret, err := o.MakeDockerSecret()
78
+				secret, err := o.NewDockerSecret()
79 79
 				cmdutil.CheckErr(err)
80 80
 
81 81
 				cmdutil.CheckErr(f.PrintObject(c, secret, out))
... ...
@@ -99,7 +98,7 @@ func NewCmdCreateDockerConfigSecret(name, fullName string, f *cmdutil.Factory, o
99 99
 }
100 100
 
101 101
 func (o CreateDockerConfigOptions) CreateDockerSecret() error {
102
-	secret, err := o.MakeDockerSecret()
102
+	secret, err := o.NewDockerSecret()
103 103
 	if err != nil {
104 104
 		return err
105 105
 	}
... ...
@@ -113,7 +112,7 @@ func (o CreateDockerConfigOptions) CreateDockerSecret() error {
113 113
 	return nil
114 114
 }
115 115
 
116
-func (o CreateDockerConfigOptions) MakeDockerSecret() (*api.Secret, error) {
116
+func (o CreateDockerConfigOptions) NewDockerSecret() (*api.Secret, error) {
117 117
 	dockercfgAuth := credentialprovider.DockerConfigEntry{
118 118
 		Username: o.Username,
119 119
 		Password: o.Password,
... ...
@@ -128,7 +127,6 @@ func (o CreateDockerConfigOptions) MakeDockerSecret() (*api.Secret, error) {
128 128
 	}
129 129
 
130 130
 	secret := &api.Secret{}
131
-	secret.Namespace = o.SecretNamespace
132 131
 	secret.Name = o.SecretName
133 132
 	secret.Type = api.SecretTypeDockercfg
134 133
 	secret.Data = map[string][]byte{}
... ...
@@ -147,20 +145,17 @@ func (o *CreateDockerConfigOptions) Complete(f *cmdutil.Factory, args []string)
147 147
 	if err != nil {
148 148
 		return err
149 149
 	}
150
-	o.SecretNamespace, _, err = f.DefaultNamespace()
150
+	namespace, _, err := f.DefaultNamespace()
151 151
 	if err != nil {
152 152
 		return err
153 153
 	}
154 154
 
155
-	o.SecretsInterface = client.Secrets(o.SecretNamespace)
155
+	o.SecretsInterface = client.Secrets(namespace)
156 156
 
157 157
 	return nil
158 158
 }
159 159
 
160 160
 func (o CreateDockerConfigOptions) Validate() error {
161
-	if len(o.SecretNamespace) == 0 {
162
-		return errors.New("secret namespace must be present")
163
-	}
164 161
 	if len(o.SecretName) == 0 {
165 162
 		return errors.New("secret name must be present")
166 163
 	}
167 164
new file mode 100644
... ...
@@ -0,0 +1,195 @@
0
+package secrets
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io"
6
+	"io/ioutil"
7
+
8
+	"k8s.io/kubernetes/pkg/api"
9
+	"k8s.io/kubernetes/pkg/client"
10
+	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
11
+
12
+	"github.com/spf13/cobra"
13
+)
14
+
15
+const (
16
+	// CreateSSHAuthSecretRecommendedCommandName represents name of subcommand for `oc secrets` command
17
+	CreateSSHAuthSecretRecommendedCommandName = "new-sshauth"
18
+
19
+	createSSHAuthSecretLong = `
20
+Create a new SSH authentication secret
21
+
22
+SSH authentication secrets are used to authenticate against SCM servers.
23
+
24
+When creating applications, you may have a SCM server that requires SSH authentication - private SSH key.
25
+In order for the nodes to clone source code on your behalf, they have to have the credentials. You can 
26
+provide this information by creating a 'sshauth' secret and attaching it to your service account.`
27
+
28
+	createSSHAuthSecretExample = `  // If your SSH authentication method requires only private SSH key, add it by using:
29
+  $ %[1]s SECRET --ssh-privatekey=FILENAME
30
+
31
+  // If your SSH authentication method requires also CA certificate, add it by using:
32
+  $ %[1]s SECRET --ssh-privatekey=FILENAME --ca-cert=FILENAME
33
+
34
+  // If you do already have a .gitconfig file needed for authentication, you can create a gitconfig secret by using:
35
+  $ %[2]s SECRET path/to/.gitconfig`
36
+)
37
+
38
+// CreateSSHAuthSecretOptions holds the credential needed to authenticate against SCM servers.
39
+type CreateSSHAuthSecretOptions struct {
40
+	SecretName      string
41
+	PrivateKeyPath  string
42
+	CertificatePath string
43
+	GitConfigPath   string
44
+
45
+	PromptForPassword bool
46
+
47
+	Out io.Writer
48
+
49
+	SecretsInterface client.SecretsInterface
50
+}
51
+
52
+// NewCmdCreateSSHAuthSecret implements the OpenShift cli secrets new-sshauth subcommand
53
+func NewCmdCreateSSHAuthSecret(name, fullName string, f *kcmdutil.Factory, out io.Writer, newSecretFullName, ocEditFullName string) *cobra.Command {
54
+	o := &CreateSSHAuthSecretOptions{
55
+		Out: out,
56
+	}
57
+
58
+	cmd := &cobra.Command{
59
+		Use:     fmt.Sprintf("%s SECRET_NAME --ssh-privatekey=FILENAME [--ca-cert=FILENAME] [--gitconfig=FILENAME]", name),
60
+		Short:   "Create a new secret for SSH authentication",
61
+		Long:    createSSHAuthSecretLong,
62
+		Example: fmt.Sprintf(createSSHAuthSecretExample, fullName, newSecretFullName, ocEditFullName),
63
+		Run: func(c *cobra.Command, args []string) {
64
+			if err := o.Complete(f, args); err != nil {
65
+				kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
66
+			}
67
+
68
+			if err := o.Validate(); err != nil {
69
+				kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
70
+			}
71
+
72
+			if len(kcmdutil.GetFlagString(c, "output")) != 0 {
73
+				secret, err := o.NewSSHAuthSecret()
74
+				kcmdutil.CheckErr(err)
75
+
76
+				kcmdutil.CheckErr(f.PrintObject(c, secret, out))
77
+				return
78
+			}
79
+
80
+			if err := o.CreateSSHAuthSecret(); err != nil {
81
+				kcmdutil.CheckErr(err)
82
+			}
83
+		},
84
+	}
85
+
86
+	cmd.Flags().StringVar(&o.PrivateKeyPath, "ssh-privatekey", "", "Path to a SSH private key")
87
+	cmd.Flags().StringVar(&o.CertificatePath, "ca-cert", "", "Path to a certificate file")
88
+	cmd.Flags().StringVar(&o.GitConfigPath, "gitconfig", "", "Path to a .gitconfig file")
89
+
90
+	// autocompletion hints
91
+	cmd.MarkFlagFilename("ssh-privatekey")
92
+	cmd.MarkFlagFilename("ca-cert")
93
+	cmd.MarkFlagFilename("gitconfig")
94
+
95
+	kcmdutil.AddPrinterFlags(cmd)
96
+
97
+	return cmd
98
+}
99
+
100
+// CreateSSHAuthSecret saves created Secret structure and prints the secret name to the output on success.
101
+func (o *CreateSSHAuthSecretOptions) CreateSSHAuthSecret() error {
102
+	secret, err := o.NewSSHAuthSecret()
103
+	if err != nil {
104
+		return err
105
+	}
106
+
107
+	if _, err := o.SecretsInterface.Create(secret); err != nil {
108
+		return err
109
+	}
110
+
111
+	fmt.Fprintf(o.GetOut(), "secret/%s\n", secret.Name)
112
+	return nil
113
+}
114
+
115
+// NewSSHAuthSecret builds up the Secret structure containing secret name, type and data structure
116
+// containing desired credentials.
117
+func (o *CreateSSHAuthSecretOptions) NewSSHAuthSecret() (*api.Secret, error) {
118
+	secret := &api.Secret{}
119
+	secret.Name = o.SecretName
120
+	secret.Type = api.SecretTypeOpaque
121
+	secret.Data = map[string][]byte{}
122
+
123
+	if len(o.PrivateKeyPath) != 0 {
124
+		privateKeyContent, err := ioutil.ReadFile(o.PrivateKeyPath)
125
+		if err != nil {
126
+			return nil, err
127
+		}
128
+		secret.Data[SourcePrivateKey] = privateKeyContent
129
+	}
130
+
131
+	if len(o.CertificatePath) != 0 {
132
+		caContent, err := ioutil.ReadFile(o.CertificatePath)
133
+		if err != nil {
134
+			return nil, err
135
+		}
136
+		secret.Data[SourceCertificate] = caContent
137
+	}
138
+
139
+	if len(o.GitConfigPath) != 0 {
140
+		gitConfig, err := ioutil.ReadFile(o.GitConfigPath)
141
+		if err != nil {
142
+			return nil, err
143
+		}
144
+		secret.Data[SourceGitConfig] = gitConfig
145
+	}
146
+
147
+	return secret, nil
148
+}
149
+
150
+// Complete fills CreateSSHAuthSecretOptions fields with data and checks whether necessary
151
+// arguments were provided.
152
+func (o *CreateSSHAuthSecretOptions) Complete(f *kcmdutil.Factory, args []string) error {
153
+	if len(args) != 1 {
154
+		return errors.New("must have exactly one argument: secret name")
155
+	}
156
+	o.SecretName = args[0]
157
+
158
+	if f != nil {
159
+		client, err := f.Client()
160
+		if err != nil {
161
+			return err
162
+		}
163
+		namespace, _, err := f.DefaultNamespace()
164
+		if err != nil {
165
+			return err
166
+		}
167
+		o.SecretsInterface = client.Secrets(namespace)
168
+	}
169
+
170
+	return nil
171
+}
172
+
173
+// Validate check if all necessary fields from CreateSSHAuthSecretOptions are present.
174
+func (o CreateSSHAuthSecretOptions) Validate() error {
175
+	if len(o.SecretName) == 0 {
176
+		return errors.New("basic authentication secret name must be present")
177
+	}
178
+
179
+	if len(o.PrivateKeyPath) == 0 {
180
+		return errors.New("must provide SSH private key")
181
+	}
182
+
183
+	return nil
184
+}
185
+
186
+// GetOut check if the CreateSSHAuthSecretOptions Out Writer is set. Returns it if the Writer
187
+// is present, if not returns Writer on which all Write calls succeed without doing anything.
188
+func (o CreateSSHAuthSecretOptions) GetOut() io.Writer {
189
+	if o.Out == nil {
190
+		return ioutil.Discard
191
+	}
192
+
193
+	return o.Out
194
+}
0 195
new file mode 100644
... ...
@@ -0,0 +1,56 @@
0
+package secrets
1
+
2
+import (
3
+	"testing"
4
+)
5
+
6
+func TestValidateSSHAuth(t *testing.T) {
7
+	tests := []struct {
8
+		testName string
9
+		args     []string
10
+		params   CreateSSHAuthSecretOptions
11
+		expErr   bool
12
+	}{
13
+		{
14
+			testName: "validArgs",
15
+			args:     []string{"testSecret"},
16
+			params: CreateSSHAuthSecretOptions{
17
+				PrivateKeyPath: "./bsFixtures/valid/ssh-privatekey",
18
+			},
19
+			expErr: false,
20
+		},
21
+		{
22
+			testName: "validArgsWithCertificate",
23
+			args:     []string{"testSecret"},
24
+			params: CreateSSHAuthSecretOptions{
25
+				PrivateKeyPath:  "./bsFixtures/valid/ssh-privatekey",
26
+				CertificatePath: "./bsFixtures/valid/ca.crt",
27
+			},
28
+			expErr: false,
29
+		},
30
+		{
31
+			testName: "noName",
32
+			args:     []string{},
33
+			params: CreateSSHAuthSecretOptions{
34
+				PrivateKeyPath:  "./bsFixtures/valid/ssh-privatekey",
35
+				CertificatePath: "./bsFixtures/valid/ca.crt",
36
+			},
37
+			expErr: true, //"Must have exactly one argument: secret name"
38
+		},
39
+		{
40
+			testName: "noParams",
41
+			args:     []string{"testSecret"},
42
+			params:   CreateSSHAuthSecretOptions{},
43
+			expErr:   true, //"Must provide SSH authentication credentials"
44
+		},
45
+	}
46
+
47
+	for _, test := range tests {
48
+		options := test.params
49
+		options.Complete(nil, test.args)
50
+		err := options.Validate()
51
+		if err != nil && !test.expErr {
52
+			t.Errorf("%s: unexpected error: %v", test.testName, err)
53
+		}
54
+	}
55
+}
... ...
@@ -5,6 +5,7 @@ import (
5 5
 
6 6
 	"github.com/spf13/cobra"
7 7
 
8
+	"github.com/openshift/origin/pkg/build/builder/cmd/scmauth"
8 9
 	cmdutil "github.com/openshift/origin/pkg/cmd/util"
9 10
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
10 11
 )
... ...
@@ -12,6 +13,19 @@ import (
12 12
 const SecretsRecommendedName = "secrets"
13 13
 
14 14
 const (
15
+	// SourceUsername is the key of the optional username for basic authentication subcommand
16
+	SourceUsername = scmauth.UsernameSecret
17
+	// SourcePassword is the key of the optional password or token for basic authentication subcommand
18
+	SourcePassword = scmauth.PasswordSecret
19
+	// SourceCertificate is the key of the optional certificate authority for basic authentication subcommand
20
+	SourceCertificate = scmauth.CACertName
21
+	// SourcePrivateKey is the key of the required SSH private key for SSH authentication subcommand
22
+	SourcePrivateKey = scmauth.SSHPrivateKeyMethodName
23
+	// SourceGitconfig is the key of the optional gitconfig content for both basic and SSH authentication subcommands
24
+	SourceGitConfig = scmauth.GitConfigName
25
+)
26
+
27
+const (
15 28
 	secretsLong = `
16 29
 Manage secrets in your project
17 30
 
... ...
@@ -20,7 +34,7 @@ They are commonly used to hold things like keys for authentication to other inte
20 20
 Docker registries.`
21 21
 )
22 22
 
23
-func NewCmdSecrets(name, fullName string, f *clientcmd.Factory, out io.Writer, ocEditFullName string) *cobra.Command {
23
+func NewCmdSecrets(name, fullName string, f *clientcmd.Factory, reader io.Reader, out io.Writer, ocEditFullName string) *cobra.Command {
24 24
 	// Parent command to which all subcommands are added.
25 25
 	cmds := &cobra.Command{
26 26
 		Use:   name,
... ...
@@ -32,6 +46,8 @@ func NewCmdSecrets(name, fullName string, f *clientcmd.Factory, out io.Writer, o
32 32
 	newSecretFullName := fullName + " " + NewSecretRecommendedCommandName
33 33
 	cmds.AddCommand(NewCmdCreateSecret(NewSecretRecommendedCommandName, newSecretFullName, f, out))
34 34
 	cmds.AddCommand(NewCmdCreateDockerConfigSecret(CreateDockerConfigSecretRecommendedName, fullName+" "+CreateDockerConfigSecretRecommendedName, f.Factory, out, newSecretFullName, ocEditFullName))
35
+	cmds.AddCommand(NewCmdCreateBasicAuthSecret(CreateBasicAuthSecretRecommendedCommandName, fullName+" "+CreateBasicAuthSecretRecommendedCommandName, f.Factory, reader, out, newSecretFullName, ocEditFullName))
36
+	cmds.AddCommand(NewCmdCreateSSHAuthSecret(CreateSSHAuthSecretRecommendedCommandName, fullName+" "+CreateSSHAuthSecretRecommendedCommandName, f.Factory, out, newSecretFullName, ocEditFullName))
35 37
 	cmds.AddCommand(NewCmdAddSecret(AddSecretRecommendedName, fullName+" "+AddSecretRecommendedName, f.Factory, out))
36 38
 
37 39
 	return cmds
... ...
@@ -1422,6 +1422,66 @@ _oc_secrets_new-dockercfg()
1422 1422
     must_have_one_noun=()
1423 1423
 }
1424 1424
 
1425
+_oc_secrets_new-basicauth()
1426
+{
1427
+    last_command="oc_secrets_new-basicauth"
1428
+    commands=()
1429
+
1430
+    flags=()
1431
+    two_word_flags=()
1432
+    flags_with_completion=()
1433
+    flags_completion=()
1434
+
1435
+    flags+=("--ca-cert=")
1436
+    flags_with_completion+=("--ca-cert")
1437
+    flags_completion+=("_filedir")
1438
+    flags+=("--gitconfig=")
1439
+    flags_with_completion+=("--gitconfig")
1440
+    flags_completion+=("_filedir")
1441
+    flags+=("--no-headers")
1442
+    flags+=("--output=")
1443
+    two_word_flags+=("-o")
1444
+    flags+=("--output-version=")
1445
+    flags+=("--password=")
1446
+    flags+=("--prompt")
1447
+    flags+=("--template=")
1448
+    two_word_flags+=("-t")
1449
+    flags+=("--username=")
1450
+
1451
+    must_have_one_flag=()
1452
+    must_have_one_noun=()
1453
+}
1454
+
1455
+_oc_secrets_new-sshauth()
1456
+{
1457
+    last_command="oc_secrets_new-sshauth"
1458
+    commands=()
1459
+
1460
+    flags=()
1461
+    two_word_flags=()
1462
+    flags_with_completion=()
1463
+    flags_completion=()
1464
+
1465
+    flags+=("--ca-cert=")
1466
+    flags_with_completion+=("--ca-cert")
1467
+    flags_completion+=("_filedir")
1468
+    flags+=("--gitconfig=")
1469
+    flags_with_completion+=("--gitconfig")
1470
+    flags_completion+=("_filedir")
1471
+    flags+=("--no-headers")
1472
+    flags+=("--output=")
1473
+    two_word_flags+=("-o")
1474
+    flags+=("--output-version=")
1475
+    flags+=("--ssh-privatekey=")
1476
+    flags_with_completion+=("--ssh-privatekey")
1477
+    flags_completion+=("_filedir")
1478
+    flags+=("--template=")
1479
+    two_word_flags+=("-t")
1480
+
1481
+    must_have_one_flag=()
1482
+    must_have_one_noun=()
1483
+}
1484
+
1425 1485
 _oc_secrets_add()
1426 1486
 {
1427 1487
     last_command="oc_secrets_add"
... ...
@@ -1444,6 +1504,8 @@ _oc_secrets()
1444 1444
     commands=()
1445 1445
     commands+=("new")
1446 1446
     commands+=("new-dockercfg")
1447
+    commands+=("new-basicauth")
1448
+    commands+=("new-sshauth")
1447 1449
     commands+=("add")
1448 1450
 
1449 1451
     flags=()
... ...
@@ -2877,6 +2877,66 @@ _openshift_cli_secrets_new-dockercfg()
2877 2877
     must_have_one_noun=()
2878 2878
 }
2879 2879
 
2880
+_openshift_cli_secrets_new-basicauth()
2881
+{
2882
+    last_command="openshift_cli_secrets_new-basicauth"
2883
+    commands=()
2884
+
2885
+    flags=()
2886
+    two_word_flags=()
2887
+    flags_with_completion=()
2888
+    flags_completion=()
2889
+
2890
+    flags+=("--ca-cert=")
2891
+    flags_with_completion+=("--ca-cert")
2892
+    flags_completion+=("_filedir")
2893
+    flags+=("--gitconfig=")
2894
+    flags_with_completion+=("--gitconfig")
2895
+    flags_completion+=("_filedir")
2896
+    flags+=("--no-headers")
2897
+    flags+=("--output=")
2898
+    two_word_flags+=("-o")
2899
+    flags+=("--output-version=")
2900
+    flags+=("--password=")
2901
+    flags+=("--prompt")
2902
+    flags+=("--template=")
2903
+    two_word_flags+=("-t")
2904
+    flags+=("--username=")
2905
+
2906
+    must_have_one_flag=()
2907
+    must_have_one_noun=()
2908
+}
2909
+
2910
+_openshift_cli_secrets_new-sshauth()
2911
+{
2912
+    last_command="openshift_cli_secrets_new-sshauth"
2913
+    commands=()
2914
+
2915
+    flags=()
2916
+    two_word_flags=()
2917
+    flags_with_completion=()
2918
+    flags_completion=()
2919
+
2920
+    flags+=("--ca-cert=")
2921
+    flags_with_completion+=("--ca-cert")
2922
+    flags_completion+=("_filedir")
2923
+    flags+=("--gitconfig=")
2924
+    flags_with_completion+=("--gitconfig")
2925
+    flags_completion+=("_filedir")
2926
+    flags+=("--no-headers")
2927
+    flags+=("--output=")
2928
+    two_word_flags+=("-o")
2929
+    flags+=("--output-version=")
2930
+    flags+=("--ssh-privatekey=")
2931
+    flags_with_completion+=("--ssh-privatekey")
2932
+    flags_completion+=("_filedir")
2933
+    flags+=("--template=")
2934
+    two_word_flags+=("-t")
2935
+
2936
+    must_have_one_flag=()
2937
+    must_have_one_noun=()
2938
+}
2939
+
2880 2940
 _openshift_cli_secrets_add()
2881 2941
 {
2882 2942
     last_command="openshift_cli_secrets_add"
... ...
@@ -2899,6 +2959,8 @@ _openshift_cli_secrets()
2899 2899
     commands=()
2900 2900
     commands+=("new")
2901 2901
     commands+=("new-dockercfg")
2902
+    commands+=("new-basicauth")
2903
+    commands+=("new-sshauth")
2902 2904
     commands+=("add")
2903 2905
 
2904 2906
     flags=()
... ...
@@ -20,8 +20,7 @@ oc secrets new from-file .dockercfg=${HOME}/dockerconfig
20 20
 [ "$(oc secrets new-dockercfg dockercfg --docker-username=sample-user --docker-password=sample-password --docker-email=fake@example.org -o yaml | grep "kubernetes.io/dockercfg")" ]
21 21
 [ "$(oc secrets new from-file .dockercfg=${HOME}/dockerconfig -o yaml | grep "kubernetes.io/dockercfg")" ]
22 22
 # check to make sure malformed names fail as expected
23
-[ "$(oc secrets new bad-name .docker=cfg=${HOME}/dockerconfig 2>&1 | grep "error: Key names or file paths cannot contain '='.")" ] 
24
-
23
+[ "$(oc secrets new bad-name .docker=cfg=${HOME}/dockerconfig 2>&1 | grep "error: Key names or file paths cannot contain '='.")" ]
25 24
 
26 25
 # attach secrets to service account
27 26
 # single secret with prefix
... ...
@@ -32,5 +31,29 @@ oc secrets add serviceaccounts/deployer secrets/dockercfg secrets/from-file
32 32
 oc secrets add serviceaccounts/deployer secrets/dockercfg secrets/from-file --for=pull
33 33
 # make sure we can add as as pull secret and mount secret at once
34 34
 oc secrets add serviceaccounts/deployer secrets/dockercfg secrets/from-file --for=pull,mount
35
-echo "secrets: ok"
36 35
 
36
+GIT_CONFIG_PATH=$(create_gitconfig)
37
+CA_CERT_PATH=$(create_valid_file ca.pem)
38
+PRIVATE_KEY_PATH=$(create_valid_file id_rsa)
39
+
40
+oc secrets new-basicauth basicauth --username=sample-user --password=sample-password --gitconfig=$GIT_CONFIG_PATH --ca-cert=$PRIVATE_KEY_PATH
41
+# check to make sure two mutual exclusive flags return error as expected
42
+[ "$(oc secrets new-basicauth bad-file --password=sample-password --prompt 2>&1 | grep "error: must provide either --prompt or --password flag")" ]
43
+# check to make sure incorrect .gitconfig path fail as expected
44
+[ "$(oc secrets new-basicauth bad-file --username=user --gitconfig=/bad/path 2>&1 | grep "error: open /bad/path: no such file or directory")" ]
45
+
46
+oc secrets new-sshauth sshauth --ssh-privatekey=$PRIVATE_KEY_PATH --ca-cert=$PRIVATE_KEY_PATH
47
+# check to make sure incorrect SSH private-key path fail as expected
48
+[ "$(oc secrets new-sshauth bad-file --ssh-privatekey=/bad/path 2>&1 | grep "error: open /bad/path: no such file or directory")" ]
49
+
50
+# attach secrets to service account
51
+# single secret with prefix
52
+oc secrets add serviceaccounts/deployer secrets/basicauth
53
+# don't add the same secret twice
54
+oc secrets add serviceaccounts/deployer secrets/basicauth secrets/sshauth
55
+# make sure we can add as as pull secret
56
+oc secrets add serviceaccounts/deployer secrets/basicauth secrets/sshauth --for=pull
57
+# make sure we can add as as pull secret and mount secret at once
58
+oc secrets add serviceaccounts/deployer secrets/basicauth secrets/sshauth --for=pull,mount
59
+
60
+echo "secrets: ok"