forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
add_secret_to_obj.go
274 lines (231 loc) · 7.01 KB
/
add_secret_to_obj.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
package secrets
import (
"errors"
"fmt"
"io"
"io/ioutil"
"strings"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
client "k8s.io/kubernetes/pkg/client/unversioned"
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/sets"
"github.com/spf13/cobra"
)
const (
AddSecretRecommendedName = "add"
// TODO: move to examples
addSecretLong = `
Add secrets to a ServiceAccount
After you have created a secret, you probably want to make use of that secret inside of a pod, for a build, or as an image pull secret. In order to do that, you must add your secret to a service account.`
addSecretExample = ` // To use your secret inside of a pod or as a push, pull, or source secret for a build, you must add a 'mount' secret to your service account like this:
$ %[1]s serviceaccount/sa-name secrets/secret-name secrets/another-secret-name
// To use your secret as an image pull secret, you must add a 'pull' secret to your service account like this:
$ %[1]s serviceaccount/sa-name secrets/secret-name --for=pull
// To use your secret for image pulls or inside a pod:
$ %[1]s serviceaccount/sa-name secrets/secret-name --for=pull,mount`
)
type AddSecretOptions struct {
TargetName string
SecretNames []string
ForMount bool
ForPull bool
Namespace string
Mapper meta.RESTMapper
Typer runtime.ObjectTyper
ClientMapper resource.ClientMapper
ClientInterface client.Interface
Out io.Writer
}
// NewCmdAddSecret creates a command object for adding a secret reference to a service account
func NewCmdAddSecret(name, fullName string, f *kcmdutil.Factory, out io.Writer) *cobra.Command {
o := &AddSecretOptions{Out: out}
var typeFlags []string
cmd := &cobra.Command{
Use: fmt.Sprintf("%s serviceaccounts/sa-name secrets/secret-name [secrets/another-secret-name]...", name),
Short: "Add secrets to a ServiceAccount",
Long: addSecretLong,
Example: fmt.Sprintf(addSecretExample, fullName),
Run: func(c *cobra.Command, args []string) {
if err := o.Complete(f, args, typeFlags); err != nil {
kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
}
if err := o.Validate(); err != nil {
kcmdutil.CheckErr(kcmdutil.UsageError(c, err.Error()))
}
if err := o.AddSecrets(); err != nil {
kcmdutil.CheckErr(err)
}
},
}
cmd.Flags().StringSliceVar(&typeFlags, "for", []string{"mount"}, "type of secret to add: mount or pull")
return cmd
}
func (o *AddSecretOptions) Complete(f *kcmdutil.Factory, args []string, typeFlags []string) error {
if len(args) < 2 {
return errors.New("must have service account name and at least one secret name")
}
o.TargetName = args[0]
o.SecretNames = args[1:]
if len(typeFlags) == 0 {
o.ForMount = true
} else {
for _, flag := range typeFlags {
loweredValue := strings.ToLower(flag)
switch loweredValue {
case "pull":
o.ForPull = true
case "mount":
o.ForMount = true
default:
return fmt.Errorf("unknown for: %v", flag)
}
}
}
var err error
o.ClientInterface, err = f.Client()
if err != nil {
return err
}
o.Namespace, _, err = f.DefaultNamespace()
if err != nil {
return err
}
o.Mapper, o.Typer = f.Object()
o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping)
return nil
}
func (o AddSecretOptions) Validate() error {
if len(o.TargetName) == 0 {
return errors.New("service account name must be present")
}
if len(o.SecretNames) == 0 {
return errors.New("secret name must be present")
}
if !o.ForPull && !o.ForMount {
return errors.New("for must be present")
}
if o.Mapper == nil {
return errors.New("Mapper must be present")
}
if o.Typer == nil {
return errors.New("Typer must be present")
}
if o.ClientMapper == nil {
return errors.New("ClientMapper must be present")
}
if o.ClientInterface == nil {
return errors.New("ClientInterface must be present")
}
return nil
}
func (o AddSecretOptions) AddSecrets() error {
r := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper, kapi.Codecs.UniversalDecoder()).
NamespaceParam(o.Namespace).
ResourceNames("serviceaccounts", o.TargetName).
SingleResourceType().
Do()
if r.Err() != nil {
return r.Err()
}
obj, err := r.Object()
if err != nil {
return err
}
switch t := obj.(type) {
case *kapi.ServiceAccount:
err = o.addSecretsToServiceAccount(t)
if err != nil {
return err
}
default:
return fmt.Errorf("unhandled object: %#v", t)
}
return nil
}
// TODO: when Secrets in kapi.ServiceAccount get changed to MountSecrets and represented by LocalObjectReferences, this can be
// refactored to reuse the addition code better
// addSecretsToServiceAccount adds secrets to the service account, either as pull secrets, mount secrets, or both.
func (o AddSecretOptions) addSecretsToServiceAccount(serviceaccount *kapi.ServiceAccount) error {
updated := false
newSecrets, err := o.getSecrets()
if err != nil {
return err
}
newSecretNames := getSecretNames(newSecrets)
if o.ForMount {
currentSecrets := getMountSecretNames(serviceaccount)
secretsToAdd := newSecretNames.Difference(currentSecrets)
for _, secretName := range secretsToAdd.List() {
serviceaccount.Secrets = append(serviceaccount.Secrets, kapi.ObjectReference{Name: secretName})
updated = true
}
}
if o.ForPull {
currentSecrets := getPullSecretNames(serviceaccount)
secretsToAdd := newSecretNames.Difference(currentSecrets)
for _, secretName := range secretsToAdd.List() {
serviceaccount.ImagePullSecrets = append(serviceaccount.ImagePullSecrets, kapi.LocalObjectReference{Name: secretName})
updated = true
}
}
if updated {
_, err = o.ClientInterface.ServiceAccounts(o.Namespace).Update(serviceaccount)
return err
}
return nil
}
func (o AddSecretOptions) getSecrets() ([]*kapi.Secret, error) {
r := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper, kapi.Codecs.UniversalDecoder()).
NamespaceParam(o.Namespace).
ResourceNames("secrets", o.SecretNames...).
SingleResourceType().
Do()
if r.Err() != nil {
return nil, r.Err()
}
infos, err := r.Infos()
if err != nil {
return nil, err
}
secrets := []*kapi.Secret{}
for i := range infos {
info := infos[i]
switch t := info.Object.(type) {
case *kapi.Secret:
secrets = append(secrets, t)
default:
return nil, fmt.Errorf("unhandled object: %#v", t)
}
}
return secrets, nil
}
func getSecretNames(secrets []*kapi.Secret) sets.String {
names := sets.String{}
for _, secret := range secrets {
names.Insert(secret.Name)
}
return names
}
func getMountSecretNames(serviceaccount *kapi.ServiceAccount) sets.String {
names := sets.String{}
for _, secret := range serviceaccount.Secrets {
names.Insert(secret.Name)
}
return names
}
func getPullSecretNames(serviceaccount *kapi.ServiceAccount) sets.String {
names := sets.String{}
for _, secret := range serviceaccount.ImagePullSecrets {
names.Insert(secret.Name)
}
return names
}
func (o AddSecretOptions) GetOut() io.Writer {
if o.Out == nil {
return ioutil.Discard
}
return o.Out
}