registry.go 20 KB

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