registry.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. package registry
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "encoding/json"
  6. "fmt"
  7. "net/http"
  8. "net/url"
  9. "strings"
  10. "time"
  11. "github.com/aws/aws-sdk-go/aws/awserr"
  12. "github.com/aws/aws-sdk-go/service/ecr"
  13. "github.com/porter-dev/porter/internal/models"
  14. "github.com/porter-dev/porter/internal/oauth"
  15. "github.com/porter-dev/porter/internal/repository"
  16. "golang.org/x/oauth2"
  17. ints "github.com/porter-dev/porter/internal/models/integrations"
  18. "github.com/digitalocean/godo"
  19. "github.com/docker/cli/cli/config/configfile"
  20. "github.com/docker/cli/cli/config/types"
  21. )
  22. // Registry wraps the gorm Registry model
  23. type Registry models.Registry
  24. // Repository is a collection of images
  25. type Repository struct {
  26. // Name of the repository
  27. Name string `json:"name"`
  28. // When the repository was created
  29. CreatedAt time.Time `json:"created_at,omitempty"`
  30. // The URI of the repository
  31. URI string `json:"uri"`
  32. }
  33. // Image is a Docker image type
  34. type Image struct {
  35. // The sha256 digest of the image manifest.
  36. Digest string `json:"digest"`
  37. // The tag used for the image.
  38. Tag string `json:"tag"`
  39. // The image manifest associated with the image.
  40. Manifest string `json:"manifest"`
  41. // The name of the repository associated with the image.
  42. RepositoryName string `json:"repository_name"`
  43. // When the image was pushed
  44. PushedAt *time.Time `json:"pushed_at"`
  45. }
  46. // ListRepositories lists the repositories for a registry
  47. func (r *Registry) ListRepositories(
  48. repo repository.Repository,
  49. doAuth *oauth2.Config, // only required if using DOCR
  50. ) ([]*Repository, error) {
  51. // switch on the auth mechanism to get a token
  52. if r.AWSIntegrationID != 0 {
  53. return r.listECRRepositories(repo)
  54. }
  55. if r.GCPIntegrationID != 0 {
  56. return r.listGCRRepositories(repo)
  57. }
  58. if r.DOIntegrationID != 0 {
  59. return r.listDOCRRepositories(repo, doAuth)
  60. }
  61. if r.BasicIntegrationID != 0 {
  62. return r.listPrivateRegistryRepositories(repo)
  63. }
  64. return nil, fmt.Errorf("error listing repositories")
  65. }
  66. type gcrJWT struct {
  67. AccessToken string `json:"token"`
  68. ExpiresInSec int `json:"expires_in"`
  69. }
  70. type gcrRepositoryResp struct {
  71. Repositories []string `json:"repositories"`
  72. }
  73. func (r *Registry) GetGCRToken(repo repository.Repository) (*ints.TokenCache, error) {
  74. getTokenCache := r.getTokenCacheFunc(repo)
  75. gcp, err := repo.GCPIntegration.ReadGCPIntegration(
  76. r.GCPIntegrationID,
  77. )
  78. if err != nil {
  79. return nil, err
  80. }
  81. // get oauth2 access token
  82. _, err = gcp.GetBearerToken(
  83. getTokenCache,
  84. r.setTokenCacheFunc(repo),
  85. "https://www.googleapis.com/auth/devstorage.read_write",
  86. )
  87. if err != nil {
  88. return nil, err
  89. }
  90. // it's now written to the token cache, so return
  91. cache, err := getTokenCache()
  92. if err != nil {
  93. return nil, err
  94. }
  95. return cache, nil
  96. }
  97. func (r *Registry) listGCRRepositories(
  98. repo repository.Repository,
  99. ) ([]*Repository, error) {
  100. gcp, err := repo.GCPIntegration.ReadGCPIntegration(
  101. r.GCPIntegrationID,
  102. )
  103. if err != nil {
  104. return nil, err
  105. }
  106. // Just use service account key to authenticate, since scopes may not be in place
  107. // for oauth. This also prevents us from making more requests.
  108. client := &http.Client{}
  109. req, err := http.NewRequest(
  110. "GET",
  111. "https://gcr.io/v2/_catalog",
  112. nil,
  113. )
  114. if err != nil {
  115. return nil, err
  116. }
  117. req.SetBasicAuth("_json_key", string(gcp.GCPKeyData))
  118. resp, err := client.Do(req)
  119. if err != nil {
  120. return nil, err
  121. }
  122. gcrResp := gcrRepositoryResp{}
  123. if err := json.NewDecoder(resp.Body).Decode(&gcrResp); err != nil {
  124. return nil, fmt.Errorf("Could not read GCR repositories: %v", err)
  125. }
  126. res := make([]*Repository, 0)
  127. parsedURL, err := url.Parse("https://" + r.URL)
  128. if err != nil {
  129. return nil, err
  130. }
  131. for _, repo := range gcrResp.Repositories {
  132. res = append(res, &Repository{
  133. Name: repo,
  134. URI: parsedURL.Host + "/" + repo,
  135. })
  136. }
  137. return res, nil
  138. }
  139. func (r *Registry) listECRRepositories(repo repository.Repository) ([]*Repository, error) {
  140. aws, err := repo.AWSIntegration.ReadAWSIntegration(
  141. r.AWSIntegrationID,
  142. )
  143. if err != nil {
  144. return nil, err
  145. }
  146. sess, err := aws.GetSession()
  147. if err != nil {
  148. return nil, err
  149. }
  150. svc := ecr.New(sess)
  151. resp, err := svc.DescribeRepositories(&ecr.DescribeRepositoriesInput{})
  152. if err != nil {
  153. return nil, err
  154. }
  155. res := make([]*Repository, 0)
  156. for _, repo := range resp.Repositories {
  157. res = append(res, &Repository{
  158. Name: *repo.RepositoryName,
  159. CreatedAt: *repo.CreatedAt,
  160. URI: *repo.RepositoryUri,
  161. })
  162. }
  163. return res, nil
  164. }
  165. func (r *Registry) listDOCRRepositories(
  166. repo repository.Repository,
  167. doAuth *oauth2.Config,
  168. ) ([]*Repository, error) {
  169. oauthInt, err := repo.OAuthIntegration.ReadOAuthIntegration(
  170. r.DOIntegrationID,
  171. )
  172. if err != nil {
  173. return nil, err
  174. }
  175. tok, _, err := oauth.GetAccessToken(oauthInt.SharedOAuthModel, doAuth, oauth.MakeUpdateOAuthIntegrationTokenFunction(oauthInt, repo))
  176. if err != nil {
  177. return nil, err
  178. }
  179. client := godo.NewFromToken(tok)
  180. urlArr := strings.Split(r.URL, "/")
  181. if len(urlArr) != 2 {
  182. return nil, fmt.Errorf("invalid digital ocean registry url")
  183. }
  184. name := urlArr[1]
  185. repos, _, err := client.Registry.ListRepositories(context.TODO(), name, &godo.ListOptions{})
  186. if err != nil {
  187. return nil, err
  188. }
  189. res := make([]*Repository, 0)
  190. for _, repo := range repos {
  191. res = append(res, &Repository{
  192. Name: repo.Name,
  193. URI: r.URL + "/" + repo.Name,
  194. })
  195. }
  196. return res, nil
  197. }
  198. func (r *Registry) listPrivateRegistryRepositories(
  199. repo repository.Repository,
  200. ) ([]*Repository, error) {
  201. // handle dockerhub different, as it doesn't implement the docker registry http api
  202. if strings.Contains(r.URL, "docker.io") {
  203. // in this case, we just return the single dockerhub repository that's linked
  204. res := make([]*Repository, 0)
  205. res = append(res, &Repository{
  206. Name: strings.Split(r.URL, "docker.io/")[1],
  207. URI: r.URL,
  208. })
  209. return res, nil
  210. }
  211. basic, err := repo.BasicIntegration.ReadBasicIntegration(
  212. r.BasicIntegrationID,
  213. )
  214. if err != nil {
  215. return nil, err
  216. }
  217. // Just use service account key to authenticate, since scopes may not be in place
  218. // for oauth. This also prevents us from making more requests.
  219. client := &http.Client{}
  220. // get the host and scheme to make the request
  221. parsedURL, err := url.Parse(r.URL)
  222. req, err := http.NewRequest(
  223. "GET",
  224. fmt.Sprintf("%s://%s/v2/_catalog", parsedURL.Scheme, parsedURL.Host),
  225. nil,
  226. )
  227. if err != nil {
  228. return nil, err
  229. }
  230. req.SetBasicAuth(string(basic.Username), string(basic.Password))
  231. resp, err := client.Do(req)
  232. if err != nil {
  233. return nil, err
  234. }
  235. // if the status code is 404, fallback to the Docker Hub implementation
  236. if resp.StatusCode == 404 {
  237. req, err := http.NewRequest(
  238. "GET",
  239. fmt.Sprintf("%s/", r.URL),
  240. nil,
  241. )
  242. if err != nil {
  243. return nil, err
  244. }
  245. req.SetBasicAuth(string(basic.Username), string(basic.Password))
  246. resp, err = client.Do(req)
  247. if err != nil {
  248. return nil, err
  249. }
  250. }
  251. gcrResp := gcrRepositoryResp{}
  252. if err := json.NewDecoder(resp.Body).Decode(&gcrResp); err != nil {
  253. return nil, fmt.Errorf("Could not read private registry repositories: %v", err)
  254. }
  255. res := make([]*Repository, 0)
  256. if err != nil {
  257. return nil, err
  258. }
  259. for _, repo := range gcrResp.Repositories {
  260. res = append(res, &Repository{
  261. Name: repo,
  262. URI: parsedURL.Host + "/" + repo,
  263. })
  264. }
  265. return res, nil
  266. }
  267. func (r *Registry) getTokenCacheFunc(
  268. repo repository.Repository,
  269. ) ints.GetTokenCacheFunc {
  270. return func() (tok *ints.TokenCache, err error) {
  271. reg, err := repo.Registry.ReadRegistry(r.ID)
  272. if err != nil {
  273. return nil, err
  274. }
  275. return &reg.TokenCache.TokenCache, nil
  276. }
  277. }
  278. func (r *Registry) setTokenCacheFunc(
  279. repo repository.Repository,
  280. ) ints.SetTokenCacheFunc {
  281. return func(token string, expiry time.Time) error {
  282. _, err := repo.Registry.UpdateRegistryTokenCache(
  283. &ints.RegTokenCache{
  284. TokenCache: ints.TokenCache{
  285. Token: []byte(token),
  286. Expiry: expiry,
  287. },
  288. RegistryID: r.ID,
  289. },
  290. )
  291. return err
  292. }
  293. }
  294. // CreateRepository creates a repository for a registry, if needed
  295. // (currently only required for ECR)
  296. func (r *Registry) CreateRepository(
  297. repo repository.Repository,
  298. name string,
  299. ) error {
  300. // if aws, create repository
  301. if r.AWSIntegrationID != 0 {
  302. return r.createECRRepository(repo, name)
  303. }
  304. // otherwise, no-op
  305. return nil
  306. }
  307. func (r *Registry) createECRRepository(
  308. repo repository.Repository,
  309. name string,
  310. ) error {
  311. aws, err := repo.AWSIntegration.ReadAWSIntegration(
  312. r.AWSIntegrationID,
  313. )
  314. if err != nil {
  315. return err
  316. }
  317. sess, err := aws.GetSession()
  318. if err != nil {
  319. return err
  320. }
  321. svc := ecr.New(sess)
  322. // determine if repository already exists
  323. _, err = svc.DescribeRepositories(&ecr.DescribeRepositoriesInput{
  324. RepositoryNames: []*string{&name},
  325. })
  326. // if the repository was not found, create it
  327. if aerr, ok := err.(awserr.Error); ok && aerr.Code() == ecr.ErrCodeRepositoryNotFoundException {
  328. _, err = svc.CreateRepository(&ecr.CreateRepositoryInput{
  329. RepositoryName: &name,
  330. })
  331. return err
  332. } else if err != nil {
  333. return err
  334. }
  335. return nil
  336. }
  337. // ListImages lists the images for an image repository
  338. func (r *Registry) ListImages(
  339. repoName string,
  340. repo repository.Repository,
  341. doAuth *oauth2.Config, // only required if using DOCR
  342. ) ([]*Image, error) {
  343. // switch on the auth mechanism to get a token
  344. if r.AWSIntegrationID != 0 {
  345. return r.listECRImages(repoName, repo)
  346. }
  347. if r.GCPIntegrationID != 0 {
  348. return r.listGCRImages(repoName, repo)
  349. }
  350. if r.DOIntegrationID != 0 {
  351. return r.listDOCRImages(repoName, repo, doAuth)
  352. }
  353. if r.BasicIntegrationID != 0 {
  354. return r.listPrivateRegistryImages(repoName, repo)
  355. }
  356. return nil, fmt.Errorf("error listing images")
  357. }
  358. func (r *Registry) listECRImages(repoName string, repo repository.Repository) ([]*Image, error) {
  359. aws, err := repo.AWSIntegration.ReadAWSIntegration(
  360. r.AWSIntegrationID,
  361. )
  362. if err != nil {
  363. return nil, err
  364. }
  365. sess, err := aws.GetSession()
  366. if err != nil {
  367. return nil, err
  368. }
  369. svc := ecr.New(sess)
  370. resp, err := svc.ListImages(&ecr.ListImagesInput{
  371. RepositoryName: &repoName,
  372. })
  373. if err != nil {
  374. return nil, err
  375. }
  376. describeResp, err := svc.DescribeImages(&ecr.DescribeImagesInput{
  377. RepositoryName: &repoName,
  378. ImageIds: resp.ImageIds,
  379. })
  380. if err != nil {
  381. return nil, err
  382. }
  383. imageDetails := describeResp.ImageDetails
  384. nextToken := describeResp.NextToken
  385. for nextToken != nil {
  386. describeResp, err := svc.DescribeImages(&ecr.DescribeImagesInput{
  387. RepositoryName: &repoName,
  388. ImageIds: resp.ImageIds,
  389. })
  390. if err != nil {
  391. return nil, err
  392. }
  393. nextToken = describeResp.NextToken
  394. imageDetails = append(imageDetails, describeResp.ImageDetails...)
  395. }
  396. res := make([]*Image, 0)
  397. for _, img := range imageDetails {
  398. for _, tag := range img.ImageTags {
  399. res = append(res, &Image{
  400. Digest: *img.ImageDigest,
  401. Tag: *tag,
  402. RepositoryName: repoName,
  403. PushedAt: img.ImagePushedAt,
  404. })
  405. }
  406. }
  407. return res, nil
  408. }
  409. type gcrImageResp struct {
  410. Tags []string `json:"tags"`
  411. }
  412. func (r *Registry) listGCRImages(repoName string, repo repository.Repository) ([]*Image, error) {
  413. gcp, err := repo.GCPIntegration.ReadGCPIntegration(
  414. r.GCPIntegrationID,
  415. )
  416. if err != nil {
  417. return nil, err
  418. }
  419. // use JWT token to request catalog
  420. client := &http.Client{}
  421. parsedURL, err := url.Parse("https://" + r.URL)
  422. if err != nil {
  423. return nil, err
  424. }
  425. trimmedPath := strings.Trim(parsedURL.Path, "/")
  426. req, err := http.NewRequest(
  427. "GET",
  428. fmt.Sprintf("https://%s/v2/%s/%s/tags/list", parsedURL.Host, trimmedPath, repoName),
  429. nil,
  430. )
  431. if err != nil {
  432. return nil, err
  433. }
  434. req.SetBasicAuth("_json_key", string(gcp.GCPKeyData))
  435. resp, err := client.Do(req)
  436. if err != nil {
  437. return nil, err
  438. }
  439. gcrResp := gcrImageResp{}
  440. if err := json.NewDecoder(resp.Body).Decode(&gcrResp); err != nil {
  441. return nil, fmt.Errorf("Could not read GCR repositories: %v", err)
  442. }
  443. res := make([]*Image, 0)
  444. for _, tag := range gcrResp.Tags {
  445. res = append(res, &Image{
  446. RepositoryName: repoName,
  447. Tag: tag,
  448. })
  449. }
  450. return res, nil
  451. }
  452. func (r *Registry) listDOCRImages(
  453. repoName string,
  454. repo repository.Repository,
  455. doAuth *oauth2.Config,
  456. ) ([]*Image, error) {
  457. oauthInt, err := repo.OAuthIntegration.ReadOAuthIntegration(
  458. r.DOIntegrationID,
  459. )
  460. if err != nil {
  461. return nil, err
  462. }
  463. tok, _, err := oauth.GetAccessToken(oauthInt.SharedOAuthModel, doAuth, oauth.MakeUpdateOAuthIntegrationTokenFunction(oauthInt, repo))
  464. if err != nil {
  465. return nil, err
  466. }
  467. client := godo.NewFromToken(tok)
  468. urlArr := strings.Split(r.URL, "/")
  469. if len(urlArr) != 2 {
  470. return nil, fmt.Errorf("invalid digital ocean registry url")
  471. }
  472. name := urlArr[1]
  473. tags, _, err := client.Registry.ListRepositoryTags(context.TODO(), name, repoName, &godo.ListOptions{})
  474. if err != nil {
  475. return nil, err
  476. }
  477. res := make([]*Image, 0)
  478. for _, tag := range tags {
  479. res = append(res, &Image{
  480. RepositoryName: repoName,
  481. Tag: tag.Tag,
  482. })
  483. }
  484. return res, nil
  485. }
  486. func (r *Registry) listPrivateRegistryImages(repoName string, repo repository.Repository) ([]*Image, error) {
  487. // handle dockerhub different, as it doesn't implement the docker registry http api
  488. if strings.Contains(r.URL, "docker.io") {
  489. return r.listDockerHubImages(repoName, repo)
  490. }
  491. basic, err := repo.BasicIntegration.ReadBasicIntegration(
  492. r.BasicIntegrationID,
  493. )
  494. if err != nil {
  495. return nil, err
  496. }
  497. // Just use service account key to authenticate, since scopes may not be in place
  498. // for oauth. This also prevents us from making more requests.
  499. client := &http.Client{}
  500. // get the host and scheme to make the request
  501. parsedURL, err := url.Parse(r.URL)
  502. req, err := http.NewRequest(
  503. "GET",
  504. fmt.Sprintf("%s://%s/v2/%s/tags/list", parsedURL.Scheme, parsedURL.Host, repoName),
  505. nil,
  506. )
  507. if err != nil {
  508. return nil, err
  509. }
  510. req.SetBasicAuth(string(basic.Username), string(basic.Password))
  511. resp, err := client.Do(req)
  512. if err != nil {
  513. return nil, err
  514. }
  515. gcrResp := gcrImageResp{}
  516. if err := json.NewDecoder(resp.Body).Decode(&gcrResp); err != nil {
  517. return nil, fmt.Errorf("Could not read private registry repositories: %v", err)
  518. }
  519. res := make([]*Image, 0)
  520. for _, tag := range gcrResp.Tags {
  521. res = append(res, &Image{
  522. RepositoryName: repoName,
  523. Tag: tag,
  524. })
  525. }
  526. return res, nil
  527. }
  528. type dockerHubImageResult struct {
  529. Name string `json:"name"`
  530. }
  531. type dockerHubImageResp struct {
  532. Results []dockerHubImageResult `json:"results"`
  533. }
  534. type dockerHubLoginReq struct {
  535. Username string `json:"username"`
  536. Password string `json:"password"`
  537. }
  538. type dockerHubLoginResp struct {
  539. Token string `json:"token"`
  540. }
  541. func (r *Registry) listDockerHubImages(repoName string, repo repository.Repository) ([]*Image, error) {
  542. basic, err := repo.BasicIntegration.ReadBasicIntegration(
  543. r.BasicIntegrationID,
  544. )
  545. if err != nil {
  546. return nil, err
  547. }
  548. client := &http.Client{}
  549. // first, make a request for the access token
  550. data, err := json.Marshal(&dockerHubLoginReq{
  551. Username: string(basic.Username),
  552. Password: string(basic.Password),
  553. })
  554. if err != nil {
  555. return nil, err
  556. }
  557. req, err := http.NewRequest(
  558. "POST",
  559. "https://hub.docker.com/v2/users/login",
  560. strings.NewReader(string(data)),
  561. )
  562. if err != nil {
  563. return nil, err
  564. }
  565. req.Header.Add("Content-Type", "application/json")
  566. resp, err := client.Do(req)
  567. if err != nil {
  568. return nil, err
  569. }
  570. tokenObj := dockerHubLoginResp{}
  571. if err := json.NewDecoder(resp.Body).Decode(&tokenObj); err != nil {
  572. return nil, fmt.Errorf("Could not decode Dockerhub token from response: %v", err)
  573. }
  574. req, err = http.NewRequest(
  575. "GET",
  576. fmt.Sprintf("https://hub.docker.com/v2/repositories/%s/tags", strings.Split(r.URL, "docker.io/")[1]),
  577. nil,
  578. )
  579. if err != nil {
  580. return nil, err
  581. }
  582. req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tokenObj.Token))
  583. resp, err = client.Do(req)
  584. if err != nil {
  585. return nil, err
  586. }
  587. imageResp := dockerHubImageResp{}
  588. if err := json.NewDecoder(resp.Body).Decode(&imageResp); err != nil {
  589. return nil, fmt.Errorf("Could not read private registry repositories: %v", err)
  590. }
  591. res := make([]*Image, 0)
  592. for _, result := range imageResp.Results {
  593. res = append(res, &Image{
  594. RepositoryName: repoName,
  595. Tag: result.Name,
  596. })
  597. }
  598. return res, nil
  599. }
  600. // GetDockerConfigJSON returns a dockerconfigjson file contents with "auths"
  601. // populated.
  602. func (r *Registry) GetDockerConfigJSON(
  603. repo repository.Repository,
  604. doAuth *oauth2.Config, // only required if using DOCR
  605. ) ([]byte, error) {
  606. var conf *configfile.ConfigFile
  607. var err error
  608. // switch on the auth mechanism to get a token
  609. if r.AWSIntegrationID != 0 {
  610. conf, err = r.getECRDockerConfigFile(repo)
  611. }
  612. if r.GCPIntegrationID != 0 {
  613. conf, err = r.getGCRDockerConfigFile(repo)
  614. }
  615. if r.DOIntegrationID != 0 {
  616. conf, err = r.getDOCRDockerConfigFile(repo, doAuth)
  617. }
  618. if r.BasicIntegrationID != 0 {
  619. conf, err = r.getPrivateRegistryDockerConfigFile(repo)
  620. }
  621. if err != nil {
  622. return nil, err
  623. }
  624. return json.Marshal(conf)
  625. }
  626. func (r *Registry) getECRDockerConfigFile(
  627. repo repository.Repository,
  628. ) (*configfile.ConfigFile, error) {
  629. aws, err := repo.AWSIntegration.ReadAWSIntegration(
  630. r.AWSIntegrationID,
  631. )
  632. if err != nil {
  633. return nil, err
  634. }
  635. sess, err := aws.GetSession()
  636. if err != nil {
  637. return nil, err
  638. }
  639. ecrSvc := ecr.New(sess)
  640. output, err := ecrSvc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{})
  641. if err != nil {
  642. return nil, err
  643. }
  644. token := *output.AuthorizationData[0].AuthorizationToken
  645. decodedToken, err := base64.StdEncoding.DecodeString(token)
  646. if err != nil {
  647. return nil, err
  648. }
  649. parts := strings.SplitN(string(decodedToken), ":", 2)
  650. if len(parts) < 2 {
  651. return nil, err
  652. }
  653. key := r.URL
  654. if !strings.Contains(key, "http") {
  655. key = "https://" + key
  656. }
  657. return &configfile.ConfigFile{
  658. AuthConfigs: map[string]types.AuthConfig{
  659. key: types.AuthConfig{
  660. Username: parts[0],
  661. Password: parts[1],
  662. Auth: token,
  663. },
  664. },
  665. }, nil
  666. }
  667. func (r *Registry) getGCRDockerConfigFile(
  668. repo repository.Repository,
  669. ) (*configfile.ConfigFile, error) {
  670. gcp, err := repo.GCPIntegration.ReadGCPIntegration(
  671. r.GCPIntegrationID,
  672. )
  673. if err != nil {
  674. return nil, err
  675. }
  676. key := r.URL
  677. if !strings.Contains(key, "http") {
  678. key = "https://" + key
  679. }
  680. parsedURL, _ := url.Parse(key)
  681. return &configfile.ConfigFile{
  682. AuthConfigs: map[string]types.AuthConfig{
  683. parsedURL.Host: types.AuthConfig{
  684. Username: "_json_key",
  685. Password: string(gcp.GCPKeyData),
  686. Auth: generateAuthToken("_json_key", string(gcp.GCPKeyData)),
  687. },
  688. },
  689. }, nil
  690. }
  691. func (r *Registry) getDOCRDockerConfigFile(
  692. repo repository.Repository,
  693. doAuth *oauth2.Config,
  694. ) (*configfile.ConfigFile, error) {
  695. oauthInt, err := repo.OAuthIntegration.ReadOAuthIntegration(
  696. r.DOIntegrationID,
  697. )
  698. if err != nil {
  699. return nil, err
  700. }
  701. tok, _, err := oauth.GetAccessToken(oauthInt.SharedOAuthModel, doAuth, oauth.MakeUpdateOAuthIntegrationTokenFunction(oauthInt, repo))
  702. if err != nil {
  703. return nil, err
  704. }
  705. key := r.URL
  706. if !strings.Contains(key, "http") {
  707. key = "https://" + key
  708. }
  709. parsedURL, _ := url.Parse(key)
  710. return &configfile.ConfigFile{
  711. AuthConfigs: map[string]types.AuthConfig{
  712. parsedURL.Host: types.AuthConfig{
  713. Username: tok,
  714. Password: tok,
  715. Auth: generateAuthToken(tok, tok),
  716. },
  717. },
  718. }, nil
  719. }
  720. func (r *Registry) getPrivateRegistryDockerConfigFile(
  721. repo repository.Repository,
  722. ) (*configfile.ConfigFile, error) {
  723. basic, err := repo.BasicIntegration.ReadBasicIntegration(
  724. r.BasicIntegrationID,
  725. )
  726. if err != nil {
  727. return nil, err
  728. }
  729. key := r.URL
  730. if !strings.Contains(key, "http") {
  731. key = "https://" + key
  732. }
  733. parsedURL, _ := url.Parse(key)
  734. authConfigKey := parsedURL.Host
  735. if strings.Contains(r.URL, "index.docker.io") {
  736. authConfigKey = "https://index.docker.io/v1/"
  737. }
  738. return &configfile.ConfigFile{
  739. AuthConfigs: map[string]types.AuthConfig{
  740. authConfigKey: types.AuthConfig{
  741. Username: string(basic.Username),
  742. Password: string(basic.Password),
  743. Auth: generateAuthToken(string(basic.Username), string(basic.Password)),
  744. },
  745. },
  746. }, nil
  747. }
  748. func generateAuthToken(username, password string) string {
  749. return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
  750. }