main.go 3.7 KB

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