14
14
package regcreds
15
15
16
16
import (
17
+ "fmt"
18
+
19
+ secretsClient "github.com/aws/amazon-ecs-cli/ecs-cli/modules/clients/aws/secretsmanager"
20
+ "github.com/aws/amazon-ecs-cli/ecs-cli/modules/commands/flags"
21
+ "github.com/aws/amazon-ecs-cli/ecs-cli/modules/config"
17
22
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils/regcreds"
23
+ "github.com/aws/aws-sdk-go/aws"
24
+ "github.com/aws/aws-sdk-go/service/secretsmanager"
25
+ "github.com/pkg/errors"
18
26
log "github.com/sirupsen/logrus"
19
27
"github.com/urfave/cli"
20
28
)
21
29
30
+ // CredsOutputEntry contains the credential ARN and associated container names
31
+ // TODO: use & move to output_reader once implemented?
32
+ type CredsOutputEntry struct {
33
+ CredentialARN string
34
+ ContainerNames []string
35
+ }
36
+
22
37
// Up creates or updates registry credential secrets and an ECS task execution role needed to use them in a task def
23
38
func Up (c * cli.Context ) {
24
39
args := c .Args ()
@@ -31,7 +46,172 @@ func Up(c *cli.Context) {
31
46
if err != nil {
32
47
log .Fatal ("Error executing 'up': " , err )
33
48
}
34
- log .Infof ("Read creds input: %+v" , credsInput ) // remove after SDK calls added
35
49
36
- //TODO: create secrets, create role, produce output
50
+ err = validateCredsInput (* credsInput )
51
+ if err != nil {
52
+ log .Fatal ("Error executing 'up': " , err )
53
+ }
54
+
55
+ commandConfig := getNewCommandConfig (c )
56
+
57
+ _ , err = getOrCreateRegistryCredentials (credsInput .RegistryCredentials , commandConfig , c )
58
+ if err != nil {
59
+ log .Fatal ("Error executing 'up': " , err )
60
+ }
61
+
62
+ //TODO: create role, produce output
63
+ }
64
+
65
+ func getOrCreateRegistryCredentials (entryMap readers.RegistryCreds , cmdConfig * config.CommandConfig , c * cli.Context ) (* map [string ]CredsOutputEntry , error ) {
66
+ registryResults := make (map [string ]CredsOutputEntry )
67
+ updateAllowed := c .Bool (flags .UpdateExistingSecretsFlag )
68
+
69
+ smClient := secretsClient .NewSecretsManagerClient (cmdConfig )
70
+
71
+ for registryName , credentialEntry := range entryMap {
72
+ hasCredPair := hasCredPair (credentialEntry )
73
+ hasSecretARN := false
74
+ if credentialEntry .SecretManagerARN != "" {
75
+ hasSecretARN = true
76
+ }
77
+
78
+ log .Infof ("Processing credentials for registry %s..." , registryName )
79
+
80
+ if hasCredPair && hasSecretARN {
81
+ arn , err := updateOrWarnForExistingSecret (credentialEntry , updateAllowed , smClient )
82
+ if err != nil {
83
+ return nil , err
84
+ }
85
+ registryResults [registryName ] = buildOutputEntry (arn , credentialEntry .ContainerNames )
86
+
87
+ } else if hasSecretARN {
88
+ registryResults [registryName ] = buildOutputEntry (credentialEntry .SecretManagerARN , credentialEntry .ContainerNames )
89
+ log .Infof ("Using existing secret %s." , registryName )
90
+
91
+ } else {
92
+ arn , err := createNewRegistrySecret (registryName , credentialEntry , smClient )
93
+ if err != nil {
94
+ return nil , err
95
+ }
96
+ registryResults [registryName ] = buildOutputEntry (arn , credentialEntry .ContainerNames )
97
+ }
98
+ }
99
+
100
+ log .Infof ("\n up results: %v" , registryResults )
101
+
102
+ return & registryResults , nil
103
+ }
104
+
105
+ func createNewRegistrySecret (registryName string , credEntry readers.RegistryCredEntry , smClient secretsClient.SMClient ) (string , error ) {
106
+
107
+ secretName := generateSecretName (registryName )
108
+
109
+ existingSecret , _ := smClient .DescribeSecret (secretName )
110
+ if existingSecret != nil {
111
+ log .Infof ("Existing credential secret found, using %s" , * existingSecret .ARN )
112
+
113
+ return * existingSecret .ARN , nil
114
+ }
115
+
116
+ secretString := generateSecretString (credEntry .Username , credEntry .Password )
117
+
118
+ createSecretRequest := secretsmanager.CreateSecretInput {
119
+ Name : aws .String (secretName ),
120
+ SecretString : aws .String (secretString ),
121
+ Description : aws .String (fmt .Sprintf ("Created with the ECS CLI for use with registry %s" , registryName )),
122
+ }
123
+ if credEntry .KmsKeyID != "" {
124
+ createSecretRequest .SetKmsKeyId (credEntry .KmsKeyID )
125
+ }
126
+
127
+ output , err := smClient .CreateSecret (createSecretRequest )
128
+ if err != nil {
129
+ return "" , err
130
+ }
131
+ log .Infof ("New credential secret created: %s" , * output .ARN )
132
+
133
+ return * output .ARN , nil
134
+ }
135
+
136
+ func updateOrWarnForExistingSecret (credEntry readers.RegistryCredEntry , updateAllowed bool , smClient secretsClient.SMClient ) (string , error ) {
137
+ secretArn := credEntry .SecretManagerARN
138
+
139
+ if updateAllowed {
140
+ updatedSecretString := generateSecretString (credEntry .Username , credEntry .Password )
141
+ putSecretValueRequest := secretsmanager.PutSecretValueInput {
142
+ SecretId : aws .String (secretArn ),
143
+ SecretString : aws .String (updatedSecretString ),
144
+ }
145
+
146
+ _ , err := smClient .PutSecretValue (putSecretValueRequest )
147
+ if err != nil {
148
+ return "" , err
149
+ }
150
+
151
+ log .Infof ("Updated existing secret %s with new value" , secretArn )
152
+
153
+ } else {
154
+ log .Warnf ("'username' and 'password' found but ignored for existing secret %s. To update existing secrets with new values, use '--update-existing-secrets' flag." , secretArn )
155
+ }
156
+
157
+ return secretArn , nil
158
+ }
159
+
160
+ func validateCredsInput (input readers.ECSRegCredsInput ) error {
161
+ // TODO: validate version?
162
+
163
+ inputRegCreds := input .RegistryCredentials
164
+
165
+ if len (inputRegCreds ) == 0 {
166
+ return errors .New ("provided credentials must contain at least one registry" )
167
+ }
168
+
169
+ for registryName , credentialEntry := range inputRegCreds {
170
+ if ! hasRequiredFields (credentialEntry ) {
171
+ return fmt .Errorf ("missing required field(s) for registry %s; registry credentials should contain existing secret ARN or username + password" , registryName )
172
+ }
173
+ }
174
+ return nil
175
+ }
176
+
177
+ func hasRequiredFields (entry readers.RegistryCredEntry ) bool {
178
+ if (entry .SecretManagerARN != "" ) || hasCredPair (entry ) {
179
+ return true
180
+ }
181
+ return false
182
+ }
183
+
184
+ func hasCredPair (entry readers.RegistryCredEntry ) bool {
185
+ if entry .Username != "" && entry .Password != "" {
186
+ return true
187
+ }
188
+ return false
189
+ }
190
+
191
+ func getNewCommandConfig (c * cli.Context ) * config.CommandConfig {
192
+ rdwr , err := config .NewReadWriter ()
193
+ if err != nil {
194
+ log .Fatal ("Error executing 'up': " , err )
195
+ }
196
+ commandConfig , err := config .NewCommandConfig (c , rdwr )
197
+ if err != nil {
198
+ log .Fatal ("Error executing 'up': " , err )
199
+ }
200
+
201
+ return commandConfig
202
+ }
203
+
204
+ func generateSecretName (regName string ) string {
205
+ return "amazon-ecs-cli-setup-" + regName
206
+ }
207
+
208
+ func generateSecretString (username , password string ) string {
209
+ return `{"username":"` + username + `"},{"password":"` + password + `"}`
210
+ }
211
+
212
+ func buildOutputEntry (arn string , containers []string ) CredsOutputEntry {
213
+ return CredsOutputEntry {
214
+ CredentialARN : arn ,
215
+ ContainerNames : containers ,
216
+ }
37
217
}
0 commit comments