registry.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. package registry
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "github.com/aws/aws-sdk-go/service/ecr"
  8. "github.com/porter-dev/porter/internal/models"
  9. "github.com/porter-dev/porter/internal/repository"
  10. ints "github.com/porter-dev/porter/internal/models/integrations"
  11. )
  12. // Registry wraps the gorm Registry model
  13. type Registry models.Registry
  14. // Repository is a collection of images
  15. type Repository struct {
  16. // Name of the repository
  17. Name string `json:"name"`
  18. // When the repository was created
  19. CreatedAt time.Time `json:"created_at,omitempty"`
  20. // The URI of the repository
  21. URI string `json:"uri"`
  22. }
  23. // Image is a Docker image type
  24. type Image struct {
  25. // The sha256 digest of the image manifest.
  26. Digest string `json:"digest"`
  27. // The tag used for the image.
  28. Tag string `json:"tag"`
  29. // The image manifest associated with the image.
  30. Manifest string `json:"manifest"`
  31. // The name of the repository associated with the image.
  32. RepositoryName string `json:"repository_name"`
  33. }
  34. // ListRepositories lists the repositories for a registry
  35. func (r *Registry) ListRepositories(repo repository.Repository) ([]*Repository, error) {
  36. // switch on the auth mechanism to get a token
  37. if r.AWSIntegrationID != 0 {
  38. return r.listECRRepositories(repo)
  39. }
  40. if r.GCPIntegrationID != 0 {
  41. return r.listGCRRepositories(repo)
  42. }
  43. return nil, fmt.Errorf("error listing repositories")
  44. }
  45. type gcrJWT struct {
  46. AccessToken string `json:"token"`
  47. ExpiresInSec int `json:"expires_in"`
  48. }
  49. type gcrRepositoryResp struct {
  50. Repositories []string `json:"repositories"`
  51. }
  52. func (r *Registry) GetGCRToken(repo repository.Repository) (*ints.TokenCache, error) {
  53. gcp, err := repo.GCPIntegration.ReadGCPIntegration(
  54. r.GCPIntegrationID,
  55. )
  56. if err != nil {
  57. return nil, err
  58. }
  59. // get oauth2 access token
  60. _, err = gcp.GetBearerToken(r.getTokenCache, r.setTokenCacheFunc(repo))
  61. if err != nil {
  62. return nil, err
  63. }
  64. // it's now written to the token cache, so return
  65. cache, err := r.getTokenCache()
  66. if err != nil {
  67. return nil, err
  68. }
  69. return cache, nil
  70. }
  71. func (r *Registry) listGCRRepositories(
  72. repo repository.Repository,
  73. ) ([]*Repository, error) {
  74. gcp, err := repo.GCPIntegration.ReadGCPIntegration(
  75. r.GCPIntegrationID,
  76. )
  77. if err != nil {
  78. return nil, err
  79. }
  80. // get oauth2 access token
  81. oauthTok, err := gcp.GetBearerToken(r.getTokenCache, r.setTokenCacheFunc(repo))
  82. if err != nil {
  83. return nil, err
  84. }
  85. // use JWT token to request catalog
  86. client := &http.Client{}
  87. req, err := http.NewRequest(
  88. "GET",
  89. "https://gcr.io/v2/_catalog",
  90. nil,
  91. )
  92. if err != nil {
  93. return nil, err
  94. }
  95. req.SetBasicAuth("oauth2accesstoken", oauthTok)
  96. // req.Header.Add("Authorization", "Bearer "+jwtTok)
  97. resp, err := client.Do(req)
  98. if err != nil {
  99. return nil, err
  100. }
  101. gcrResp := gcrRepositoryResp{}
  102. if err := json.NewDecoder(resp.Body).Decode(&gcrResp); err != nil {
  103. return nil, fmt.Errorf("Could not read GCR repositories: %v", err)
  104. }
  105. res := make([]*Repository, 0)
  106. for _, repo := range gcrResp.Repositories {
  107. res = append(res, &Repository{
  108. Name: repo,
  109. })
  110. }
  111. return res, nil
  112. }
  113. func (r *Registry) listECRRepositories(repo repository.Repository) ([]*Repository, error) {
  114. aws, err := repo.AWSIntegration.ReadAWSIntegration(
  115. r.AWSIntegrationID,
  116. )
  117. if err != nil {
  118. return nil, err
  119. }
  120. sess, err := aws.GetSession()
  121. if err != nil {
  122. return nil, err
  123. }
  124. svc := ecr.New(sess)
  125. resp, err := svc.DescribeRepositories(&ecr.DescribeRepositoriesInput{})
  126. if err != nil {
  127. return nil, err
  128. }
  129. res := make([]*Repository, 0)
  130. for _, repo := range resp.Repositories {
  131. res = append(res, &Repository{
  132. Name: *repo.RepositoryName,
  133. CreatedAt: *repo.CreatedAt,
  134. URI: *repo.RepositoryUri,
  135. })
  136. }
  137. return res, nil
  138. }
  139. func (r *Registry) getTokenCache() (tok *ints.TokenCache, err error) {
  140. return &ints.TokenCache{
  141. Token: r.TokenCache.Token,
  142. Expiry: r.TokenCache.Expiry,
  143. }, nil
  144. }
  145. func (r *Registry) setTokenCacheFunc(
  146. repo repository.Repository,
  147. ) ints.SetTokenCacheFunc {
  148. return func(token string, expiry time.Time) error {
  149. _, err := repo.Registry.UpdateRegistryTokenCache(
  150. &ints.RegTokenCache{
  151. TokenCache: ints.TokenCache{
  152. Token: []byte(token),
  153. Expiry: expiry,
  154. },
  155. RegistryID: r.ID,
  156. },
  157. )
  158. return err
  159. }
  160. }
  161. // ListImages lists the images for an image repository
  162. func (r *Registry) ListImages(
  163. repoName string,
  164. repo repository.Repository,
  165. ) ([]*Image, error) {
  166. // switch on the auth mechanism to get a token
  167. if r.AWSIntegrationID != 0 {
  168. return r.listECRImages(repoName, repo)
  169. }
  170. if r.GCPIntegrationID != 0 {
  171. return r.listGCRImages(repoName, repo)
  172. }
  173. return nil, fmt.Errorf("error listing images")
  174. }
  175. func (r *Registry) listECRImages(repoName string, repo repository.Repository) ([]*Image, error) {
  176. aws, err := repo.AWSIntegration.ReadAWSIntegration(
  177. r.AWSIntegrationID,
  178. )
  179. if err != nil {
  180. return nil, err
  181. }
  182. sess, err := aws.GetSession()
  183. if err != nil {
  184. return nil, err
  185. }
  186. svc := ecr.New(sess)
  187. resp, err := svc.ListImages(&ecr.ListImagesInput{
  188. RepositoryName: &repoName,
  189. })
  190. if err != nil {
  191. return nil, err
  192. }
  193. res := make([]*Image, 0)
  194. for _, img := range resp.ImageIds {
  195. res = append(res, &Image{
  196. Digest: *img.ImageDigest,
  197. Tag: *img.ImageTag,
  198. RepositoryName: repoName,
  199. })
  200. }
  201. return res, nil
  202. }
  203. type gcrImageResp struct {
  204. Tags []string `json:"tags"`
  205. }
  206. func (r *Registry) listGCRImages(repoName string, repo repository.Repository) ([]*Image, error) {
  207. gcp, err := repo.GCPIntegration.ReadGCPIntegration(
  208. r.GCPIntegrationID,
  209. )
  210. if err != nil {
  211. return nil, err
  212. }
  213. // get oauth2 access token
  214. oauthTok, err := gcp.GetBearerToken(r.getTokenCache, r.setTokenCacheFunc(repo))
  215. if err != nil {
  216. return nil, err
  217. }
  218. // use JWT token to request catalog
  219. client := &http.Client{}
  220. req, err := http.NewRequest(
  221. "GET",
  222. fmt.Sprintf("https://gcr.io/v2/%s/tags/list", repoName),
  223. nil,
  224. )
  225. if err != nil {
  226. return nil, err
  227. }
  228. req.SetBasicAuth("oauth2accesstoken", oauthTok)
  229. resp, err := client.Do(req)
  230. if err != nil {
  231. return nil, err
  232. }
  233. gcrResp := gcrImageResp{}
  234. if err := json.NewDecoder(resp.Body).Decode(&gcrResp); err != nil {
  235. return nil, fmt.Errorf("Could not read GCR repositories: %v", err)
  236. }
  237. res := make([]*Image, 0)
  238. for _, tag := range gcrResp.Tags {
  239. res = append(res, &Image{
  240. RepositoryName: repoName,
  241. Tag: tag,
  242. })
  243. }
  244. return res, nil
  245. }