Browse code

added commands to manage serviceaccounts

Steve Kuznetsov authored on 2016/02/25 06:09:54
Showing 8 changed files
... ...
@@ -2242,6 +2242,128 @@ _oc_secrets()
2242 2242
     must_have_one_noun=()
2243 2243
 }
2244 2244
 
2245
+_oc_serviceaccounts_get-token()
2246
+{
2247
+    last_command="oc_serviceaccounts_get-token"
2248
+    commands=()
2249
+
2250
+    flags=()
2251
+    two_word_flags=()
2252
+    flags_with_completion=()
2253
+    flags_completion=()
2254
+
2255
+    flags+=("--api-version=")
2256
+    flags+=("--certificate-authority=")
2257
+    flags_with_completion+=("--certificate-authority")
2258
+    flags_completion+=("_filedir")
2259
+    flags+=("--client-certificate=")
2260
+    flags_with_completion+=("--client-certificate")
2261
+    flags_completion+=("_filedir")
2262
+    flags+=("--client-key=")
2263
+    flags_with_completion+=("--client-key")
2264
+    flags_completion+=("_filedir")
2265
+    flags+=("--cluster=")
2266
+    flags+=("--config=")
2267
+    flags_with_completion+=("--config")
2268
+    flags_completion+=("_filedir")
2269
+    flags+=("--context=")
2270
+    flags+=("--google-json-key=")
2271
+    flags+=("--insecure-skip-tls-verify")
2272
+    flags+=("--log-flush-frequency=")
2273
+    flags+=("--match-server-version")
2274
+    flags+=("--namespace=")
2275
+    two_word_flags+=("-n")
2276
+    flags+=("--server=")
2277
+    flags+=("--token=")
2278
+    flags+=("--user=")
2279
+
2280
+    must_have_one_flag=()
2281
+    must_have_one_noun=()
2282
+}
2283
+
2284
+_oc_serviceaccounts_new-token()
2285
+{
2286
+    last_command="oc_serviceaccounts_new-token"
2287
+    commands=()
2288
+
2289
+    flags=()
2290
+    two_word_flags=()
2291
+    flags_with_completion=()
2292
+    flags_completion=()
2293
+
2294
+    flags+=("--labels=")
2295
+    two_word_flags+=("-l")
2296
+    flags+=("--timeout=")
2297
+    flags+=("--api-version=")
2298
+    flags+=("--certificate-authority=")
2299
+    flags_with_completion+=("--certificate-authority")
2300
+    flags_completion+=("_filedir")
2301
+    flags+=("--client-certificate=")
2302
+    flags_with_completion+=("--client-certificate")
2303
+    flags_completion+=("_filedir")
2304
+    flags+=("--client-key=")
2305
+    flags_with_completion+=("--client-key")
2306
+    flags_completion+=("_filedir")
2307
+    flags+=("--cluster=")
2308
+    flags+=("--config=")
2309
+    flags_with_completion+=("--config")
2310
+    flags_completion+=("_filedir")
2311
+    flags+=("--context=")
2312
+    flags+=("--google-json-key=")
2313
+    flags+=("--insecure-skip-tls-verify")
2314
+    flags+=("--log-flush-frequency=")
2315
+    flags+=("--match-server-version")
2316
+    flags+=("--namespace=")
2317
+    two_word_flags+=("-n")
2318
+    flags+=("--server=")
2319
+    flags+=("--token=")
2320
+    flags+=("--user=")
2321
+
2322
+    must_have_one_flag=()
2323
+    must_have_one_noun=()
2324
+}
2325
+
2326
+_oc_serviceaccounts()
2327
+{
2328
+    last_command="oc_serviceaccounts"
2329
+    commands=()
2330
+    commands+=("get-token")
2331
+    commands+=("new-token")
2332
+
2333
+    flags=()
2334
+    two_word_flags=()
2335
+    flags_with_completion=()
2336
+    flags_completion=()
2337
+
2338
+    flags+=("--api-version=")
2339
+    flags+=("--certificate-authority=")
2340
+    flags_with_completion+=("--certificate-authority")
2341
+    flags_completion+=("_filedir")
2342
+    flags+=("--client-certificate=")
2343
+    flags_with_completion+=("--client-certificate")
2344
+    flags_completion+=("_filedir")
2345
+    flags+=("--client-key=")
2346
+    flags_with_completion+=("--client-key")
2347
+    flags_completion+=("_filedir")
2348
+    flags+=("--cluster=")
2349
+    flags+=("--config=")
2350
+    flags_with_completion+=("--config")
2351
+    flags_completion+=("_filedir")
2352
+    flags+=("--context=")
2353
+    flags+=("--google-json-key=")
2354
+    flags+=("--insecure-skip-tls-verify")
2355
+    flags+=("--log-flush-frequency=")
2356
+    flags+=("--match-server-version")
2357
+    flags+=("--namespace=")
2358
+    two_word_flags+=("-n")
2359
+    flags+=("--server=")
2360
+    flags+=("--token=")
2361
+    flags+=("--user=")
2362
+
2363
+    must_have_one_flag=()
2364
+    must_have_one_noun=()
2365
+}
2366
+
2245 2367
 _oc_logs()
2246 2368
 {
2247 2369
     last_command="oc_logs"
... ...
@@ -5810,6 +5932,53 @@ _oc_create_secret()
5810 5810
     must_have_one_noun=()
5811 5811
 }
5812 5812
 
5813
+_oc_create_serviceaccount()
5814
+{
5815
+    last_command="oc_create_serviceaccount"
5816
+    commands=()
5817
+
5818
+    flags=()
5819
+    two_word_flags=()
5820
+    flags_with_completion=()
5821
+    flags_completion=()
5822
+
5823
+    flags+=("--dry-run")
5824
+    flags+=("--generator=")
5825
+    flags+=("--output=")
5826
+    two_word_flags+=("-o")
5827
+    flags+=("--output-version=")
5828
+    flags+=("--save-config")
5829
+    flags+=("--schema-cache-dir=")
5830
+    flags+=("--validate")
5831
+    flags+=("--api-version=")
5832
+    flags+=("--certificate-authority=")
5833
+    flags_with_completion+=("--certificate-authority")
5834
+    flags_completion+=("_filedir")
5835
+    flags+=("--client-certificate=")
5836
+    flags_with_completion+=("--client-certificate")
5837
+    flags_completion+=("_filedir")
5838
+    flags+=("--client-key=")
5839
+    flags_with_completion+=("--client-key")
5840
+    flags_completion+=("_filedir")
5841
+    flags+=("--cluster=")
5842
+    flags+=("--config=")
5843
+    flags_with_completion+=("--config")
5844
+    flags_completion+=("_filedir")
5845
+    flags+=("--context=")
5846
+    flags+=("--google-json-key=")
5847
+    flags+=("--insecure-skip-tls-verify")
5848
+    flags+=("--log-flush-frequency=")
5849
+    flags+=("--match-server-version")
5850
+    flags+=("--namespace=")
5851
+    two_word_flags+=("-n")
5852
+    flags+=("--server=")
5853
+    flags+=("--token=")
5854
+    flags+=("--user=")
5855
+
5856
+    must_have_one_flag=()
5857
+    must_have_one_noun=()
5858
+}
5859
+
5813 5860
 _oc_create_route_edge()
5814 5861
 {
5815 5862
     last_command="oc_create_route_edge"
... ...
@@ -6009,6 +6178,7 @@ _oc_create()
6009 6009
     commands=()
6010 6010
     commands+=("namespace")
6011 6011
     commands+=("secret")
6012
+    commands+=("serviceaccount")
6012 6013
     commands+=("route")
6013 6014
 
6014 6015
     flags=()
... ...
@@ -7349,6 +7519,7 @@ _oc()
7349 7349
     commands+=("scale")
7350 7350
     commands+=("autoscale")
7351 7351
     commands+=("secrets")
7352
+    commands+=("serviceaccounts")
7352 7353
     commands+=("logs")
7353 7354
     commands+=("rsh")
7354 7355
     commands+=("rsync")
... ...
@@ -5579,6 +5579,128 @@ _openshift_cli_secrets()
5579 5579
     must_have_one_noun=()
5580 5580
 }
5581 5581
 
5582
+_openshift_cli_serviceaccounts_get-token()
5583
+{
5584
+    last_command="openshift_cli_serviceaccounts_get-token"
5585
+    commands=()
5586
+
5587
+    flags=()
5588
+    two_word_flags=()
5589
+    flags_with_completion=()
5590
+    flags_completion=()
5591
+
5592
+    flags+=("--api-version=")
5593
+    flags+=("--certificate-authority=")
5594
+    flags_with_completion+=("--certificate-authority")
5595
+    flags_completion+=("_filedir")
5596
+    flags+=("--client-certificate=")
5597
+    flags_with_completion+=("--client-certificate")
5598
+    flags_completion+=("_filedir")
5599
+    flags+=("--client-key=")
5600
+    flags_with_completion+=("--client-key")
5601
+    flags_completion+=("_filedir")
5602
+    flags+=("--cluster=")
5603
+    flags+=("--config=")
5604
+    flags_with_completion+=("--config")
5605
+    flags_completion+=("_filedir")
5606
+    flags+=("--context=")
5607
+    flags+=("--google-json-key=")
5608
+    flags+=("--insecure-skip-tls-verify")
5609
+    flags+=("--log-flush-frequency=")
5610
+    flags+=("--match-server-version")
5611
+    flags+=("--namespace=")
5612
+    two_word_flags+=("-n")
5613
+    flags+=("--server=")
5614
+    flags+=("--token=")
5615
+    flags+=("--user=")
5616
+
5617
+    must_have_one_flag=()
5618
+    must_have_one_noun=()
5619
+}
5620
+
5621
+_openshift_cli_serviceaccounts_new-token()
5622
+{
5623
+    last_command="openshift_cli_serviceaccounts_new-token"
5624
+    commands=()
5625
+
5626
+    flags=()
5627
+    two_word_flags=()
5628
+    flags_with_completion=()
5629
+    flags_completion=()
5630
+
5631
+    flags+=("--labels=")
5632
+    two_word_flags+=("-l")
5633
+    flags+=("--timeout=")
5634
+    flags+=("--api-version=")
5635
+    flags+=("--certificate-authority=")
5636
+    flags_with_completion+=("--certificate-authority")
5637
+    flags_completion+=("_filedir")
5638
+    flags+=("--client-certificate=")
5639
+    flags_with_completion+=("--client-certificate")
5640
+    flags_completion+=("_filedir")
5641
+    flags+=("--client-key=")
5642
+    flags_with_completion+=("--client-key")
5643
+    flags_completion+=("_filedir")
5644
+    flags+=("--cluster=")
5645
+    flags+=("--config=")
5646
+    flags_with_completion+=("--config")
5647
+    flags_completion+=("_filedir")
5648
+    flags+=("--context=")
5649
+    flags+=("--google-json-key=")
5650
+    flags+=("--insecure-skip-tls-verify")
5651
+    flags+=("--log-flush-frequency=")
5652
+    flags+=("--match-server-version")
5653
+    flags+=("--namespace=")
5654
+    two_word_flags+=("-n")
5655
+    flags+=("--server=")
5656
+    flags+=("--token=")
5657
+    flags+=("--user=")
5658
+
5659
+    must_have_one_flag=()
5660
+    must_have_one_noun=()
5661
+}
5662
+
5663
+_openshift_cli_serviceaccounts()
5664
+{
5665
+    last_command="openshift_cli_serviceaccounts"
5666
+    commands=()
5667
+    commands+=("get-token")
5668
+    commands+=("new-token")
5669
+
5670
+    flags=()
5671
+    two_word_flags=()
5672
+    flags_with_completion=()
5673
+    flags_completion=()
5674
+
5675
+    flags+=("--api-version=")
5676
+    flags+=("--certificate-authority=")
5677
+    flags_with_completion+=("--certificate-authority")
5678
+    flags_completion+=("_filedir")
5679
+    flags+=("--client-certificate=")
5680
+    flags_with_completion+=("--client-certificate")
5681
+    flags_completion+=("_filedir")
5682
+    flags+=("--client-key=")
5683
+    flags_with_completion+=("--client-key")
5684
+    flags_completion+=("_filedir")
5685
+    flags+=("--cluster=")
5686
+    flags+=("--config=")
5687
+    flags_with_completion+=("--config")
5688
+    flags_completion+=("_filedir")
5689
+    flags+=("--context=")
5690
+    flags+=("--google-json-key=")
5691
+    flags+=("--insecure-skip-tls-verify")
5692
+    flags+=("--log-flush-frequency=")
5693
+    flags+=("--match-server-version")
5694
+    flags+=("--namespace=")
5695
+    two_word_flags+=("-n")
5696
+    flags+=("--server=")
5697
+    flags+=("--token=")
5698
+    flags+=("--user=")
5699
+
5700
+    must_have_one_flag=()
5701
+    must_have_one_noun=()
5702
+}
5703
+
5582 5704
 _openshift_cli_logs()
5583 5705
 {
5584 5706
     last_command="openshift_cli_logs"
... ...
@@ -9147,6 +9269,53 @@ _openshift_cli_create_secret()
9147 9147
     must_have_one_noun=()
9148 9148
 }
9149 9149
 
9150
+_openshift_cli_create_serviceaccount()
9151
+{
9152
+    last_command="openshift_cli_create_serviceaccount"
9153
+    commands=()
9154
+
9155
+    flags=()
9156
+    two_word_flags=()
9157
+    flags_with_completion=()
9158
+    flags_completion=()
9159
+
9160
+    flags+=("--dry-run")
9161
+    flags+=("--generator=")
9162
+    flags+=("--output=")
9163
+    two_word_flags+=("-o")
9164
+    flags+=("--output-version=")
9165
+    flags+=("--save-config")
9166
+    flags+=("--schema-cache-dir=")
9167
+    flags+=("--validate")
9168
+    flags+=("--api-version=")
9169
+    flags+=("--certificate-authority=")
9170
+    flags_with_completion+=("--certificate-authority")
9171
+    flags_completion+=("_filedir")
9172
+    flags+=("--client-certificate=")
9173
+    flags_with_completion+=("--client-certificate")
9174
+    flags_completion+=("_filedir")
9175
+    flags+=("--client-key=")
9176
+    flags_with_completion+=("--client-key")
9177
+    flags_completion+=("_filedir")
9178
+    flags+=("--cluster=")
9179
+    flags+=("--config=")
9180
+    flags_with_completion+=("--config")
9181
+    flags_completion+=("_filedir")
9182
+    flags+=("--context=")
9183
+    flags+=("--google-json-key=")
9184
+    flags+=("--insecure-skip-tls-verify")
9185
+    flags+=("--log-flush-frequency=")
9186
+    flags+=("--match-server-version")
9187
+    flags+=("--namespace=")
9188
+    two_word_flags+=("-n")
9189
+    flags+=("--server=")
9190
+    flags+=("--token=")
9191
+    flags+=("--user=")
9192
+
9193
+    must_have_one_flag=()
9194
+    must_have_one_noun=()
9195
+}
9196
+
9150 9197
 _openshift_cli_create_route_edge()
9151 9198
 {
9152 9199
     last_command="openshift_cli_create_route_edge"
... ...
@@ -9346,6 +9515,7 @@ _openshift_cli_create()
9346 9346
     commands=()
9347 9347
     commands+=("namespace")
9348 9348
     commands+=("secret")
9349
+    commands+=("serviceaccount")
9349 9350
     commands+=("route")
9350 9351
 
9351 9352
     flags=()
... ...
@@ -10686,6 +10856,7 @@ _openshift_cli()
10686 10686
     commands+=("scale")
10687 10687
     commands+=("autoscale")
10688 10688
     commands+=("secrets")
10689
+    commands+=("serviceaccounts")
10689 10690
     commands+=("logs")
10690 10691
     commands+=("rsh")
10691 10692
     commands+=("rsync")
... ...
@@ -11129,12 +11300,61 @@ _openshift_kube_create_secret()
11129 11129
     must_have_one_noun=()
11130 11130
 }
11131 11131
 
11132
+_openshift_kube_create_serviceaccount()
11133
+{
11134
+    last_command="openshift_kube_create_serviceaccount"
11135
+    commands=()
11136
+
11137
+    flags=()
11138
+    two_word_flags=()
11139
+    flags_with_completion=()
11140
+    flags_completion=()
11141
+
11142
+    flags+=("--dry-run")
11143
+    flags+=("--generator=")
11144
+    flags+=("--output=")
11145
+    two_word_flags+=("-o")
11146
+    flags+=("--output-version=")
11147
+    flags+=("--save-config")
11148
+    flags+=("--schema-cache-dir=")
11149
+    flags+=("--validate")
11150
+    flags+=("--api-version=")
11151
+    flags+=("--certificate-authority=")
11152
+    flags_with_completion+=("--certificate-authority")
11153
+    flags_completion+=("_filedir")
11154
+    flags+=("--client-certificate=")
11155
+    flags_with_completion+=("--client-certificate")
11156
+    flags_completion+=("_filedir")
11157
+    flags+=("--client-key=")
11158
+    flags_with_completion+=("--client-key")
11159
+    flags_completion+=("_filedir")
11160
+    flags+=("--cluster=")
11161
+    flags+=("--config=")
11162
+    flags_with_completion+=("--config")
11163
+    flags_completion+=("_filedir")
11164
+    flags+=("--context=")
11165
+    flags+=("--google-json-key=")
11166
+    flags+=("--insecure-skip-tls-verify")
11167
+    flags+=("--kubeconfig=")
11168
+    flags+=("--log-flush-frequency=")
11169
+    flags+=("--match-server-version")
11170
+    flags+=("--namespace=")
11171
+    two_word_flags+=("-n")
11172
+    flags+=("--server=")
11173
+    flags+=("--token=")
11174
+    flags+=("--user=")
11175
+
11176
+    must_have_one_flag=()
11177
+    must_have_one_noun=()
11178
+}
11179
+
11132 11180
 _openshift_kube_create()
11133 11181
 {
11134 11182
     last_command="openshift_kube_create"
11135 11183
     commands=()
11136 11184
     commands+=("namespace")
11137 11185
     commands+=("secret")
11186
+    commands+=("serviceaccount")
11138 11187
 
11139 11188
     flags=()
11140 11189
     two_word_flags=()
... ...
@@ -841,6 +841,19 @@ Create a secret from a local file, directory or literal value.
841 841
 ====
842 842
 
843 843
 
844
+== oc create serviceaccount
845
+Create a service account with the specified name.
846
+
847
+====
848
+
849
+[options="nowrap"]
850
+----
851
+  # Create a new service account named my-service-account
852
+  $ kubectl create serviceaccount my-service-account
853
+----
854
+====
855
+
856
+
844 857
 == oc debug
845 858
 Launch a new instance of a pod for debugging
846 859
 
... ...
@@ -1625,6 +1638,38 @@ Create a new secret for SSH authentication
1625 1625
 ====
1626 1626
 
1627 1627
 
1628
+== oc serviceaccounts get-token
1629
+Get a token assigned to a service account.
1630
+
1631
+====
1632
+
1633
+[options="nowrap"]
1634
+----
1635
+  # Get the service account token from service account 'default'
1636
+  $ oc serviceaccounts get-token 'default'
1637
+
1638
+----
1639
+====
1640
+
1641
+
1642
+== oc serviceaccounts new-token
1643
+Generate a new token for a service account.
1644
+
1645
+====
1646
+
1647
+[options="nowrap"]
1648
+----
1649
+  # Generate a new token for service account 'default'
1650
+  $ oc serviceaccounts new-token 'default'
1651
+
1652
+  # Generate a new token for service account 'default' and apply 
1653
+  # labels 'foo' and 'bar' to the new token for identification
1654
+  # oc serviceaccounts new-token 'default' --labels foo=foo-value,bar=bar-value
1655
+
1656
+----
1657
+====
1658
+
1659
+
1628 1660
 == oc set env
1629 1661
 Update environment variables on a pod template
1630 1662
 
... ...
@@ -18,6 +18,7 @@ import (
18 18
 	"github.com/openshift/origin/pkg/cmd/cli/cmd/rsync"
19 19
 	"github.com/openshift/origin/pkg/cmd/cli/cmd/set"
20 20
 	"github.com/openshift/origin/pkg/cmd/cli/policy"
21
+	"github.com/openshift/origin/pkg/cmd/cli/sa"
21 22
 	"github.com/openshift/origin/pkg/cmd/cli/secrets"
22 23
 	"github.com/openshift/origin/pkg/cmd/flagtypes"
23 24
 	"github.com/openshift/origin/pkg/cmd/templates"
... ...
@@ -122,6 +123,7 @@ func NewCommandCLI(name, fullName string, in io.Reader, out, errout io.Writer) *
122 122
 				cmd.NewCmdScale(fullName, f, out),
123 123
 				cmd.NewCmdAutoscale(fullName, f, out),
124 124
 				secrets.NewCmdSecrets(secrets.SecretsRecommendedName, fullName+" "+secrets.SecretsRecommendedName, f, in, out, fullName+" edit"),
125
+				sa.NewCmdServiceAccounts(sa.ServiceAccountsRecommendedName, fullName+" "+sa.ServiceAccountsRecommendedName, f, out),
125 126
 			},
126 127
 		},
127 128
 		{
128 129
new file mode 100644
... ...
@@ -0,0 +1,139 @@
0
+package sa
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io"
6
+	"os"
7
+
8
+	"github.com/spf13/cobra"
9
+
10
+	kapi "k8s.io/kubernetes/pkg/api"
11
+	"k8s.io/kubernetes/pkg/client/unversioned"
12
+	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
13
+
14
+	"github.com/openshift/origin/pkg/cmd/util"
15
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
16
+	"github.com/openshift/origin/pkg/serviceaccounts"
17
+)
18
+
19
+const (
20
+	GetServiceAccountTokenRecommendedName = "get-token"
21
+
22
+	getServiceAccountTokenShort = `Get a token assigned to a service account.`
23
+
24
+	getServiceAccountTokenLong = `
25
+Get a token assigned to a service account.
26
+
27
+If the service account has multiple tokens, the first token found will be returned.
28
+
29
+Service account API tokens are used by service accounts to authenticate to the API.
30
+Client actions using a service account token will be executed as if the service account
31
+itself were making the actions.
32
+`
33
+
34
+	getServiceAccountTokenUsage = `%s SA-NAME`
35
+
36
+	getServiceAccountTokenExamples = `  # Get the service account token from service account 'default'
37
+  $ %[1]s 'default'
38
+`
39
+)
40
+
41
+type GetServiceAccountTokenOptions struct {
42
+	SAName        string
43
+	SAClient      unversioned.ServiceAccountsInterface
44
+	SecretsClient unversioned.SecretsInterface
45
+
46
+	Out io.Writer
47
+	Err io.Writer
48
+}
49
+
50
+func NewCommandGetServiceAccountToken(name, fullname string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
51
+	options := &GetServiceAccountTokenOptions{
52
+		Out: out,
53
+		Err: os.Stderr,
54
+	}
55
+
56
+	getServiceAccountTokenCommand := &cobra.Command{
57
+		Use:     fmt.Sprintf(getServiceAccountTokenUsage, name),
58
+		Short:   getServiceAccountTokenShort,
59
+		Long:    getServiceAccountTokenLong,
60
+		Example: fmt.Sprintf(getServiceAccountTokenExamples, fullname),
61
+		Run: func(cmd *cobra.Command, args []string) {
62
+			cmdutil.CheckErr(options.Complete(args, f, cmd))
63
+
64
+			cmdutil.CheckErr(options.Validate())
65
+
66
+			cmdutil.CheckErr(options.Run())
67
+		},
68
+	}
69
+
70
+	return getServiceAccountTokenCommand
71
+}
72
+
73
+func (o *GetServiceAccountTokenOptions) Complete(args []string, f *clientcmd.Factory, cmd *cobra.Command) error {
74
+	if len(args) != 1 {
75
+		return cmdutil.UsageError(cmd, fmt.Sprintf("expected one service account name as an argument, got %q", args))
76
+	}
77
+
78
+	o.SAName = args[0]
79
+
80
+	client, err := f.Client()
81
+	if err != nil {
82
+		return err
83
+	}
84
+
85
+	namespace, _, err := f.DefaultNamespace()
86
+	if err != nil {
87
+		return err
88
+	}
89
+
90
+	o.SAClient = client.ServiceAccounts(namespace)
91
+	o.SecretsClient = client.Secrets(namespace)
92
+	return nil
93
+}
94
+
95
+func (o *GetServiceAccountTokenOptions) Validate() error {
96
+	if o.SAName == "" {
97
+		return errors.New("service account name cannot be empty")
98
+	}
99
+
100
+	if o.SAClient == nil || o.SecretsClient == nil {
101
+		return errors.New("API clients must not be nil in order to create a new service account token")
102
+	}
103
+
104
+	if o.Out == nil || o.Err == nil {
105
+		return errors.New("cannot proceed if output or error writers are nil")
106
+	}
107
+
108
+	return nil
109
+}
110
+
111
+func (o *GetServiceAccountTokenOptions) Run() error {
112
+	serviceAccount, err := o.SAClient.Get(o.SAName)
113
+	if err != nil {
114
+		return err
115
+	}
116
+
117
+	for _, reference := range serviceAccount.Secrets {
118
+		secret, err := o.SecretsClient.Get(reference.Name)
119
+		if err != nil {
120
+			continue
121
+		}
122
+
123
+		if serviceaccounts.IsValidServiceAccountToken(serviceAccount, secret) {
124
+			token, exists := secret.Data[kapi.ServiceAccountTokenKey]
125
+			if !exists {
126
+				return fmt.Errorf("service account token %q for service account %q did not contain token data", secret.Name, serviceAccount.Name)
127
+			}
128
+
129
+			fmt.Fprintf(o.Out, string(token))
130
+			if util.IsTerminalWriter(o.Out) {
131
+				// pretty-print for a TTY
132
+				fmt.Fprintf(o.Out, "\n")
133
+			}
134
+			return nil
135
+		}
136
+	}
137
+	return fmt.Errorf("could not find a service account token for service account %q", serviceAccount.Name)
138
+}
0 139
new file mode 100644
... ...
@@ -0,0 +1,235 @@
0
+package sa
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io"
6
+	"math"
7
+	"os"
8
+	"time"
9
+
10
+	"github.com/spf13/cobra"
11
+
12
+	"k8s.io/kubernetes/pkg/api"
13
+	"k8s.io/kubernetes/pkg/client/unversioned"
14
+	"k8s.io/kubernetes/pkg/fields"
15
+	"k8s.io/kubernetes/pkg/kubectl"
16
+	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
17
+	"k8s.io/kubernetes/pkg/watch"
18
+
19
+	"github.com/openshift/origin/pkg/cmd/cli/cmd"
20
+	"github.com/openshift/origin/pkg/cmd/util"
21
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
22
+	"github.com/openshift/origin/pkg/serviceaccounts"
23
+	osautil "github.com/openshift/origin/pkg/serviceaccounts/util"
24
+)
25
+
26
+const (
27
+	NewServiceAccountTokenRecommendedName = "new-token"
28
+
29
+	newServiceAccountTokenShort = `Generate a new token for a service account.`
30
+
31
+	newServiceAccountTokenLong = `
32
+Generate a new token for a service account.
33
+
34
+Service account API tokens are used by service accounts to authenticate to the API.
35
+This command will generate a new token, which could be used to compartmentalize service
36
+account actions by executing them with distinct tokens, to rotate out pre-existing token
37
+on the service account, or for use by an external client. If a label is provided, it will
38
+be applied to any created token so that tokens created with this command can be idenitifed.
39
+`
40
+
41
+	newServiceAccountTokenUsage = `%s SA-NAME`
42
+
43
+	newServiceAccountTokenExamples = `  # Generate a new token for service account 'default'
44
+  $ %[1]s 'default'
45
+
46
+  # Generate a new token for service account 'default' and apply 
47
+  # labels 'foo' and 'bar' to the new token for identification
48
+  # %[1]s 'default' --labels foo=foo-value,bar=bar-value
49
+`
50
+)
51
+
52
+type NewServiceAccountTokenOptions struct {
53
+	SAName        string
54
+	SAClient      unversioned.ServiceAccountsInterface
55
+	SecretsClient unversioned.SecretsInterface
56
+
57
+	Labels map[string]string
58
+
59
+	Timeout time.Duration
60
+
61
+	Out io.Writer
62
+	Err io.Writer
63
+}
64
+
65
+func NewCommandNewServiceAccountToken(name, fullname string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
66
+	options := &NewServiceAccountTokenOptions{
67
+		Out:    out,
68
+		Err:    os.Stderr,
69
+		Labels: map[string]string{},
70
+	}
71
+
72
+	var requestedLabels string
73
+	newServiceAccountTokenCommand := &cobra.Command{
74
+		Use:     fmt.Sprintf(newServiceAccountTokenUsage, name),
75
+		Short:   newServiceAccountTokenShort,
76
+		Long:    newServiceAccountTokenLong,
77
+		Example: fmt.Sprintf(newServiceAccountTokenExamples, fullname),
78
+		Run: func(cmd *cobra.Command, args []string) {
79
+			cmdutil.CheckErr(options.Complete(args, requestedLabels, f, cmd))
80
+
81
+			cmdutil.CheckErr(options.Validate())
82
+
83
+			cmdutil.CheckErr(options.Run())
84
+		},
85
+	}
86
+
87
+	newServiceAccountTokenCommand.Flags().DurationVar(&options.Timeout, "timeout", 30*time.Second, "the maximum time allowed to generate a token")
88
+	newServiceAccountTokenCommand.Flags().StringVarP(&requestedLabels, "labels", "l", "", "labels to set in all resources for this application, given as a comma-delimited list of key-value pairs")
89
+	return newServiceAccountTokenCommand
90
+}
91
+
92
+func (o *NewServiceAccountTokenOptions) Complete(args []string, requestedLabels string, f *clientcmd.Factory, cmd *cobra.Command) error {
93
+	if len(args) != 1 {
94
+		return cmdutil.UsageError(cmd, fmt.Sprintf("expected one service account name as an argument, got %q", args))
95
+	}
96
+
97
+	o.SAName = args[0]
98
+
99
+	if len(requestedLabels) > 0 {
100
+		labels, err := kubectl.ParseLabels(requestedLabels)
101
+		if err != nil {
102
+			return cmdutil.UsageError(cmd, err.Error())
103
+		}
104
+		o.Labels = labels
105
+	}
106
+
107
+	client, err := f.Client()
108
+	if err != nil {
109
+		return err
110
+	}
111
+
112
+	namespace, _, err := f.DefaultNamespace()
113
+	if err != nil {
114
+		return fmt.Errorf("could not retrieve default namespace: %v", err)
115
+	}
116
+
117
+	o.SAClient = client.ServiceAccounts(namespace)
118
+	o.SecretsClient = client.Secrets(namespace)
119
+	return nil
120
+}
121
+
122
+func (o *NewServiceAccountTokenOptions) Validate() error {
123
+	if o.SAName == "" {
124
+		return errors.New("service account name cannot be empty")
125
+	}
126
+
127
+	if o.SAClient == nil || o.SecretsClient == nil {
128
+		return errors.New("API clients must not be nil in order to create a new service account token")
129
+	}
130
+
131
+	if o.Timeout <= 0 {
132
+		return errors.New("a positive amount of time must be allotted for the timeout")
133
+	}
134
+
135
+	if o.Out == nil || o.Err == nil {
136
+		return errors.New("cannot proceed if output or error writers are nil")
137
+	}
138
+
139
+	return nil
140
+}
141
+
142
+// Run creates a new token secret, waits for the service account token controller to fulfill it, then adds the token to the service account
143
+func (o *NewServiceAccountTokenOptions) Run() error {
144
+	serviceAccount, err := o.SAClient.Get(o.SAName)
145
+	if err != nil {
146
+		return err
147
+	}
148
+
149
+	tokenSecret := &api.Secret{
150
+		ObjectMeta: api.ObjectMeta{
151
+			GenerateName: osautil.GetTokenSecretNamePrefix(serviceAccount),
152
+			Namespace:    serviceAccount.Namespace,
153
+			Labels:       o.Labels,
154
+			Annotations: map[string]string{
155
+				api.ServiceAccountNameKey: serviceAccount.Name,
156
+			},
157
+		},
158
+		Type: api.SecretTypeServiceAccountToken,
159
+		Data: map[string][]byte{},
160
+	}
161
+
162
+	persistedToken, err := o.SecretsClient.Create(tokenSecret)
163
+	if err != nil {
164
+		return err
165
+	}
166
+
167
+	// we need to wait for the service account token controller to make the new token valid
168
+	tokenSecret, err = waitForToken(persistedToken, serviceAccount, o.Timeout, o.SecretsClient)
169
+	if err != nil {
170
+		return err
171
+	}
172
+
173
+	token, exists := tokenSecret.Data[api.ServiceAccountTokenKey]
174
+	if !exists {
175
+		return fmt.Errorf("service account token %q did not contain token data", tokenSecret.Name)
176
+	}
177
+
178
+	fmt.Fprintf(o.Out, string(token))
179
+	if util.IsTerminalWriter(o.Out) {
180
+		// pretty-print for a TTY
181
+		fmt.Fprintf(o.Out, "\n")
182
+	}
183
+	return nil
184
+}
185
+
186
+// waitForToken uses `cmd.Until` to wait for the service account controller to fulfill the token request
187
+func waitForToken(token *api.Secret, serviceAccount *api.ServiceAccount, timeout time.Duration, client unversioned.SecretsInterface) (*api.Secret, error) {
188
+	// there is no provided rounding function, so we use Round(x) === Floor(x + 0.5)
189
+	timeoutSeconds := int64(math.Floor(timeout.Seconds() + 0.5))
190
+
191
+	options := api.ListOptions{
192
+		FieldSelector:   fields.SelectorFromSet(fields.Set(map[string]string{"metadata.name": token.Name})),
193
+		Watch:           true,
194
+		ResourceVersion: token.ResourceVersion,
195
+		TimeoutSeconds:  &timeoutSeconds,
196
+	}
197
+
198
+	watcher, err := client.Watch(options)
199
+	if err != nil {
200
+		return nil, fmt.Errorf("could not begin watch for token: %v", err)
201
+	}
202
+
203
+	event, err := cmd.Until(timeout, watcher, func(event watch.Event) (bool, error) {
204
+		if event.Type == watch.Error {
205
+			return false, fmt.Errorf("encountered error while watching for token: %v", event.Object)
206
+		}
207
+
208
+		eventToken, ok := event.Object.(*api.Secret)
209
+		if !ok {
210
+			return false, nil
211
+		}
212
+
213
+		if eventToken.Name != token.Name {
214
+			return false, nil
215
+		}
216
+
217
+		switch event.Type {
218
+		case watch.Modified:
219
+			if serviceaccounts.IsValidServiceAccountToken(serviceAccount, eventToken) {
220
+				return true, nil
221
+			}
222
+		case watch.Deleted:
223
+			return false, errors.New("token was deleted before fulfillment by service account token controller")
224
+		case watch.Added:
225
+			return false, errors.New("unxepected action: token was added after initial creation")
226
+		}
227
+		return false, nil
228
+	})
229
+	if err != nil {
230
+		return nil, err
231
+	}
232
+
233
+	return event.Object.(*api.Secret), nil
234
+}
0 235
new file mode 100644
... ...
@@ -0,0 +1,36 @@
0
+package sa
1
+
2
+import (
3
+	"io"
4
+
5
+	"github.com/spf13/cobra"
6
+
7
+	cmdutil "github.com/openshift/origin/pkg/cmd/util"
8
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
9
+)
10
+
11
+const ServiceAccountsRecommendedName = "serviceaccounts"
12
+
13
+const (
14
+	serviceAccountsShort = `Manage service accounts in your project.`
15
+
16
+	serviceAccountsLong = `
17
+Manage service accounts in your project.
18
+
19
+Service accounts allow system components to access the API.`
20
+)
21
+
22
+func NewCmdServiceAccounts(name, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
23
+	cmds := &cobra.Command{
24
+		Use:     name,
25
+		Short:   serviceAccountsShort,
26
+		Long:    serviceAccountsLong,
27
+		Aliases: []string{"sa"},
28
+		Run:     cmdutil.DefaultSubCommandRun(out),
29
+	}
30
+
31
+	cmds.AddCommand(NewCommandGetServiceAccountToken(GetServiceAccountTokenRecommendedName, fullName+" "+GetServiceAccountTokenRecommendedName, f, out))
32
+	cmds.AddCommand(NewCommandNewServiceAccountToken(NewServiceAccountTokenRecommendedName, fullName+" "+NewServiceAccountTokenRecommendedName, f, out))
33
+
34
+	return cmds
35
+}
... ...
@@ -319,3 +319,21 @@ os::cmd::expect_success_and_not_text "oc get clusterrolebindings/cluster-admins
319 319
 os::cmd::expect_success_and_not_text "oc get rolebindings/cluster-admin         --output-version=v1 --template='{{.subjects}}' -n default" 'cascaded-group'
320 320
 os::cmd::expect_success_and_not_text "oc get scc/restricted                     --output-version=v1 --template='{{.groups}}'"              'cascaded-group'
321 321
 echo "user-group-cascade: ok"
322
+
323
+# create a new service account
324
+os::cmd::expect_success_and_text 'oc create serviceaccount my-sa-name' 'serviceaccount "my-sa-name" created'
325
+os::cmd::expect_success 'oc get sa my-sa-name'
326
+
327
+# extract token and ensure it links us back to the service account
328
+os::cmd::expect_success_and_text 'oc get user/~ --token="$( oc sa get-token my-sa-name )"' 'system:serviceaccount:.+:my-sa-name'
329
+
330
+# add a new token and ensure it links us back to the service account
331
+os::cmd::expect_success_and_text 'oc get user/~ --token="$( oc sa new-token my-sa-name )"' 'system:serviceaccount:.+:my-sa-name'
332
+
333
+# add a new labeled token and ensure the label stuck
334
+os::cmd::expect_success 'oc sa new-token my-sa-name --labels="mykey=myvalue,myotherkey=myothervalue"'
335
+os::cmd::expect_success_and_text 'oc get secrets --selector="mykey=myvalue"' 'my-sa-name'
336
+os::cmd::expect_success_and_text 'oc get secrets --selector="myotherkey=myothervalue"' 'my-sa-name'
337
+os::cmd::expect_success_and_text 'oc get secrets --selector="mykey=myvalue,myotherkey=myothervalue"' 'my-sa-name'
338
+
339
+echo "serviceacounts: ok"
322 340
\ No newline at end of file