2
0

docker.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. package config
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "encoding/json"
  6. "fmt"
  7. "io/ioutil"
  8. "net/url"
  9. "os"
  10. "os/exec"
  11. "path/filepath"
  12. "strings"
  13. "github.com/docker/cli/cli/config/configfile"
  14. "github.com/docker/cli/cli/config/types"
  15. "github.com/fatih/color"
  16. api "github.com/porter-dev/porter/api/client"
  17. "github.com/porter-dev/porter/cli/cmd/github"
  18. )
  19. func SetDockerConfig(client *api.Client) error {
  20. pID := GetCLIConfig().Project
  21. // get all registries that should be added
  22. regToAdd := make([]string, 0)
  23. // get the list of namespaces
  24. resp, err := client.ListRegistries(
  25. context.Background(),
  26. pID,
  27. )
  28. if err != nil {
  29. return err
  30. }
  31. registries := *resp
  32. for _, registry := range registries {
  33. if registry.URL != "" {
  34. rURL := registry.URL
  35. if !strings.Contains(rURL, "http") {
  36. rURL = "http://" + rURL
  37. }
  38. // strip the protocol
  39. regURL, err := url.Parse(rURL)
  40. if err != nil {
  41. continue
  42. }
  43. regToAdd = append(regToAdd, regURL.Host)
  44. }
  45. }
  46. // create a docker dir if it does not exist
  47. dockerDir := filepath.Join(home, ".docker")
  48. if _, err := os.Stat(dockerDir); os.IsNotExist(err) {
  49. err = os.Mkdir(dockerDir, 0o700)
  50. if err != nil {
  51. return err
  52. }
  53. }
  54. dockerConfigFile := filepath.Join(home, ".docker", "config.json")
  55. // determine if configfile exists
  56. if _, err := os.Stat(dockerConfigFile); os.IsNotExist(err) {
  57. // if it does not exist, create it
  58. err := ioutil.WriteFile(dockerConfigFile, []byte("{}"), 0o700)
  59. if err != nil {
  60. return err
  61. }
  62. }
  63. // read the file bytes
  64. configBytes, err := ioutil.ReadFile(dockerConfigFile)
  65. if err != nil {
  66. return err
  67. }
  68. // check if the docker credential helper exists
  69. if !commandExists("docker-credential-porter") {
  70. err := downloadCredMatchingRelease()
  71. if err != nil {
  72. color.New(color.FgRed).Println("Failed to download credential helper binary:", err.Error())
  73. os.Exit(1)
  74. }
  75. }
  76. // otherwise, check the version flag of the binary
  77. cmdVersionCred := exec.Command("docker-credential-porter", "--version")
  78. writer := &VersionWriter{}
  79. cmdVersionCred.Stdout = writer
  80. err = cmdVersionCred.Run()
  81. if err != nil || writer.Version != Version {
  82. err := downloadCredMatchingRelease()
  83. if err != nil {
  84. color.New(color.FgRed).Println("Failed to download credential helper binary:", err.Error())
  85. os.Exit(1)
  86. }
  87. }
  88. configFile := &configfile.ConfigFile{
  89. Filename: dockerConfigFile,
  90. }
  91. err = json.Unmarshal(configBytes, GetCLIConfig())
  92. if err != nil {
  93. return err
  94. }
  95. if configFile.CredentialHelpers == nil {
  96. configFile.CredentialHelpers = make(map[string]string)
  97. }
  98. if configFile.AuthConfigs == nil {
  99. configFile.AuthConfigs = make(map[string]types.AuthConfig)
  100. }
  101. for _, regURL := range regToAdd {
  102. // if this is a dockerhub registry, see if an auth config has already been generated
  103. // for index.docker.io
  104. if strings.Contains(regURL, "index.docker.io") {
  105. isAuthenticated := false
  106. for key := range configFile.AuthConfigs {
  107. if key == "https://index.docker.io/v1/" {
  108. isAuthenticated = true
  109. }
  110. }
  111. if !isAuthenticated {
  112. // get a dockerhub token from the Porter API
  113. tokenResp, err := client.GetDockerhubAuthorizationToken(context.Background(), GetCLIConfig().Project)
  114. if err != nil {
  115. return err
  116. }
  117. decodedToken, err := base64.StdEncoding.DecodeString(tokenResp.Token)
  118. if err != nil {
  119. return fmt.Errorf("Invalid token: %v", err)
  120. }
  121. parts := strings.SplitN(string(decodedToken), ":", 2)
  122. if len(parts) < 2 {
  123. return fmt.Errorf("Invalid token: expected two parts, got %d", len(parts))
  124. }
  125. configFile.AuthConfigs["https://index.docker.io/v1/"] = types.AuthConfig{
  126. Auth: tokenResp.Token,
  127. Username: parts[0],
  128. Password: parts[1],
  129. }
  130. // since we're using token-based auth, unset the credstore
  131. configFile.CredentialsStore = ""
  132. }
  133. } else {
  134. configFile.CredentialHelpers[regURL] = "porter"
  135. }
  136. }
  137. return configFile.Save()
  138. }
  139. func commandExists(cmd string) bool {
  140. _, err := exec.LookPath(cmd)
  141. return err == nil
  142. }
  143. func downloadCredMatchingRelease() error {
  144. // download the porter cred helper
  145. z := &github.ZIPReleaseGetter{
  146. AssetName: "docker-credential-porter",
  147. AssetFolderDest: "/usr/local/bin",
  148. ZipFolderDest: filepath.Join(home, ".porter"),
  149. ZipName: "docker-credential-porter_latest.zip",
  150. EntityID: "porter-dev",
  151. RepoName: "porter",
  152. IsPlatformDependent: true,
  153. Downloader: &github.ZIPDownloader{
  154. ZipFolderDest: filepath.Join(home, ".porter"),
  155. AssetFolderDest: "/usr/local/bin",
  156. ZipName: "docker-credential-porter_latest.zip",
  157. },
  158. }
  159. return z.GetRelease(Version)
  160. }