context.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*
  2. Copyright 2020 Docker, Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package ecs
  14. import (
  15. "context"
  16. "fmt"
  17. "os"
  18. "reflect"
  19. "strings"
  20. "github.com/AlecAivazis/survey/v2/terminal"
  21. "github.com/aws/aws-sdk-go/aws/awserr"
  22. "github.com/aws/aws-sdk-go/aws/credentials"
  23. "github.com/aws/aws-sdk-go/aws/defaults"
  24. "gopkg.in/ini.v1"
  25. "github.com/docker/api/context/store"
  26. "github.com/docker/api/prompt"
  27. )
  28. type contextCreateAWSHelper struct {
  29. user prompt.UI
  30. }
  31. func newContextCreateHelper() contextCreateAWSHelper {
  32. return contextCreateAWSHelper{
  33. user: prompt.User{},
  34. }
  35. }
  36. func (h contextCreateAWSHelper) createContextData(_ context.Context, opts ContextParams) (interface{}, string, error) {
  37. accessKey := opts.AwsID
  38. secretKey := opts.AwsSecret
  39. ecsCtx := store.EcsContext{
  40. Profile: opts.Profile,
  41. Region: opts.Region,
  42. }
  43. if h.missingRequiredFlags(ecsCtx) {
  44. profilesList, err := h.getProfiles()
  45. if err != nil {
  46. return nil, "", err
  47. }
  48. // get profile
  49. _, ok := profilesList[ecsCtx.Profile]
  50. if !ok {
  51. profile, err := h.chooseProfile(profilesList)
  52. if err != nil {
  53. return nil, "", err
  54. }
  55. ecsCtx.Profile = profile
  56. }
  57. // set region
  58. region, err := h.chooseRegion(ecsCtx.Region, profilesList[ecsCtx.Profile])
  59. if err != nil {
  60. return nil, "", err
  61. }
  62. ecsCtx.Region = region
  63. accessKey, secretKey, err = h.askCredentials()
  64. if err != nil {
  65. return nil, "", err
  66. }
  67. }
  68. if accessKey != "" && secretKey != "" {
  69. if err := h.saveCredentials(ecsCtx.Profile, accessKey, secretKey); err != nil {
  70. return nil, "", err
  71. }
  72. }
  73. description := ecsCtx.Region
  74. if opts.Description != "" {
  75. description = fmt.Sprintf("%s (%s)", opts.Description, description)
  76. }
  77. return ecsCtx, description, nil
  78. }
  79. func (h contextCreateAWSHelper) missingRequiredFlags(ctx store.EcsContext) bool {
  80. if ctx.Profile == "" || ctx.Region == "" {
  81. return true
  82. }
  83. return false
  84. }
  85. func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID string, secretAccessKey string) error {
  86. p := credentials.SharedCredentialsProvider{Profile: profile}
  87. _, err := p.Retrieve()
  88. if err == nil {
  89. return fmt.Errorf("credentials already exists!")
  90. }
  91. if err.(awserr.Error).Code() == "SharedCredsLoad" && err.(awserr.Error).Message() == "failed to load shared credentials file" {
  92. _, err := os.Create(p.Filename)
  93. if err != nil {
  94. return err
  95. }
  96. }
  97. credIni, err := ini.Load(p.Filename)
  98. if err != nil {
  99. return err
  100. }
  101. section, err := credIni.NewSection(profile)
  102. if err != nil {
  103. return err
  104. }
  105. _, err = section.NewKey("aws_access_key_id", accessKeyID)
  106. if err != nil {
  107. return err
  108. }
  109. _, err = section.NewKey("aws_secret_access_key", secretAccessKey)
  110. if err != nil {
  111. return err
  112. }
  113. return credIni.SaveTo(p.Filename)
  114. }
  115. func (h contextCreateAWSHelper) getProfiles() (map[string]ini.Section, error) {
  116. profiles := map[string]ini.Section{"new profile": {}}
  117. credIni, err := ini.Load(defaults.SharedConfigFilename())
  118. if err != nil {
  119. return nil, err
  120. }
  121. for _, section := range credIni.Sections() {
  122. if strings.HasPrefix(section.Name(), "profile") {
  123. profiles[section.Name()[len("profile "):]] = *section
  124. }
  125. }
  126. return profiles, nil
  127. }
  128. func (h contextCreateAWSHelper) chooseProfile(section map[string]ini.Section) (string, error) {
  129. keys := reflect.ValueOf(section).MapKeys()
  130. profiles := make([]string, len(keys))
  131. for i := 0; i < len(keys); i++ {
  132. profiles[i] = keys[i].String()
  133. }
  134. selected, err := h.user.Select("Select AWS Profile", profiles)
  135. if err != nil {
  136. if err == terminal.InterruptErr {
  137. os.Exit(-1)
  138. }
  139. return "", err
  140. }
  141. profile := profiles[selected]
  142. if profiles[selected] == "new profile" {
  143. return h.user.Input("profile name", "")
  144. }
  145. return profile, nil
  146. }
  147. func (h contextCreateAWSHelper) chooseRegion(region string, section ini.Section) (string, error) {
  148. defaultRegion := region
  149. if defaultRegion == "" && section.Name() != "" {
  150. reg, err := section.GetKey("region")
  151. if err == nil {
  152. defaultRegion = reg.Value()
  153. }
  154. }
  155. result, err := h.user.Input("Region", defaultRegion)
  156. if err != nil {
  157. return "", err
  158. }
  159. return result, nil
  160. }
  161. func (h contextCreateAWSHelper) askCredentials() (string, string, error) {
  162. confirm, err := h.user.Confirm("Enter credentials", false)
  163. if err != nil {
  164. return "", "", err
  165. }
  166. if confirm {
  167. accessKeyID, err := h.user.Input("AWS Access Key ID", "")
  168. if err != nil {
  169. return "", "", err
  170. }
  171. secretAccessKey, err := h.user.Password("Enter AWS Secret Access Key")
  172. if err != nil {
  173. return "", "", err
  174. }
  175. // validate password
  176. if len(secretAccessKey) < 3 {
  177. return "", "", fmt.Errorf("AWS Secret Access Key must have more than 3 characters")
  178. }
  179. return accessKeyID, secretAccessKey, nil
  180. }
  181. return "", "", nil
  182. }