main.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "path"
  8. "path/filepath"
  9. resource "github.com/concourse/registry-image-resource"
  10. color "github.com/fatih/color"
  11. "github.com/google/go-containerregistry/pkg/authn"
  12. "github.com/google/go-containerregistry/pkg/name"
  13. "github.com/google/go-containerregistry/pkg/v1"
  14. "github.com/google/go-containerregistry/pkg/v1/remote"
  15. "github.com/google/go-containerregistry/pkg/v1/tarball"
  16. "github.com/sirupsen/logrus"
  17. )
  18. type InRequest struct {
  19. Source resource.Source `json:"source"`
  20. Params resource.GetParams `json:"params"`
  21. Version resource.Version `json:"version"`
  22. }
  23. type InResponse struct {
  24. Version resource.Version `json:"version"`
  25. Metadata []resource.MetadataField `json:"metadata"`
  26. }
  27. type ImageMetadata struct {
  28. Env []string `json:"env"`
  29. User string `json:"user"`
  30. }
  31. func main() {
  32. logrus.SetOutput(os.Stderr)
  33. logrus.SetFormatter(&logrus.TextFormatter{
  34. ForceColors: true,
  35. })
  36. color.NoColor = false
  37. var req InRequest
  38. decoder := json.NewDecoder(os.Stdin)
  39. decoder.DisallowUnknownFields()
  40. err := decoder.Decode(&req)
  41. if err != nil {
  42. logrus.Errorf("invalid payload: %s", err)
  43. os.Exit(1)
  44. return
  45. }
  46. if req.Source.Debug {
  47. logrus.SetLevel(logrus.DebugLevel)
  48. }
  49. if len(os.Args) < 2 {
  50. logrus.Errorf("destination path not specified")
  51. os.Exit(1)
  52. return
  53. }
  54. dest := os.Args[1]
  55. ref := req.Source.Repository + "@" + req.Version.Digest
  56. n, err := name.ParseReference(ref, name.WeakValidation)
  57. if err != nil {
  58. logrus.Errorf("failed to resolve name: %s", err)
  59. os.Exit(1)
  60. return
  61. }
  62. fmt.Fprintf(os.Stderr, "fetching %s@%s\n", color.GreenString(req.Source.Repository), color.YellowString(req.Version.Digest))
  63. auth := &authn.Basic{
  64. Username: req.Source.Username,
  65. Password: req.Source.Password,
  66. }
  67. var image v1.Image
  68. if auth.Username != "" && auth.Password != "" {
  69. image, err = remote.Image(n, remote.WithAuth(auth))
  70. } else {
  71. image, err = remote.Image(n)
  72. }
  73. if err != nil {
  74. logrus.Errorf("failed to locate remote image: %s", err)
  75. os.Exit(1)
  76. return
  77. }
  78. switch req.Params.Format() {
  79. case "oci":
  80. ociFormat(dest, req, image)
  81. case "rootfs":
  82. rootfsFormat(dest, req, image)
  83. }
  84. err = saveDigest(dest, image)
  85. if err != nil {
  86. logrus.Errorf("failed to save image digest: %s", err)
  87. os.Exit(1)
  88. return
  89. }
  90. json.NewEncoder(os.Stdout).Encode(InResponse{
  91. Version: req.Version,
  92. Metadata: []resource.MetadataField{},
  93. })
  94. }
  95. func saveDigest(dest string, image v1.Image) error {
  96. digest, err := image.Digest()
  97. if err != nil {
  98. return err
  99. }
  100. digestDest := path.Join(dest, "digest")
  101. return ioutil.WriteFile(digestDest, []byte(digest.String()), 0644)
  102. }
  103. func ociFormat(dest string, req InRequest, image v1.Image) {
  104. tag, err := name.NewTag(req.Source.Name(), name.WeakValidation)
  105. if err != nil {
  106. logrus.Errorf("failed to construct tag reference: %s", err)
  107. os.Exit(1)
  108. return
  109. }
  110. err = tarball.WriteToFile(filepath.Join(dest, "image.tar"), tag, image)
  111. if err != nil {
  112. logrus.Errorf("failed to write OCI image: %s", err)
  113. os.Exit(1)
  114. return
  115. }
  116. }
  117. func rootfsFormat(dest string, req InRequest, image v1.Image) {
  118. err := unpackImage(filepath.Join(dest, "rootfs"), image, req.Source.Debug)
  119. if err != nil {
  120. logrus.Errorf("failed to extract image: %s", err)
  121. os.Exit(1)
  122. return
  123. }
  124. cfg, err := image.ConfigFile()
  125. if err != nil {
  126. logrus.Errorf("failed to inspect image config: %s", err)
  127. os.Exit(1)
  128. return
  129. }
  130. meta, err := os.Create(filepath.Join(dest, "metadata.json"))
  131. if err != nil {
  132. logrus.Errorf("failed to create image metadata: %s", err)
  133. os.Exit(1)
  134. return
  135. }
  136. env := cfg.Config.Env
  137. if len(env) == 0 {
  138. env = cfg.ContainerConfig.Env
  139. }
  140. user := cfg.Config.User
  141. if user == "" {
  142. user = cfg.ContainerConfig.User
  143. }
  144. err = json.NewEncoder(meta).Encode(ImageMetadata{
  145. Env: env,
  146. User: user,
  147. })
  148. if err != nil {
  149. logrus.Errorf("failed to write image metadata: %s", err)
  150. os.Exit(1)
  151. return
  152. }
  153. err = meta.Close()
  154. if err != nil {
  155. logrus.Errorf("failed to close image metadata file: %s", err)
  156. os.Exit(1)
  157. return
  158. }
  159. }