main.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. package main
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "os"
  6. "path/filepath"
  7. "github.com/fatih/color"
  8. "github.com/google/go-containerregistry/pkg/authn"
  9. "github.com/google/go-containerregistry/pkg/name"
  10. "github.com/google/go-containerregistry/pkg/v1"
  11. "github.com/google/go-containerregistry/pkg/v1/partial"
  12. "github.com/google/go-containerregistry/pkg/v1/remote"
  13. "github.com/google/go-containerregistry/pkg/v1/tarball"
  14. "github.com/sirupsen/logrus"
  15. resource "github.com/concourse/registry-image-resource"
  16. )
  17. type OutRequest struct {
  18. Source resource.Source `json:"source"`
  19. Params resource.PutParams `json:"params"`
  20. }
  21. type OutResponse struct {
  22. Version resource.Version `json:"version"`
  23. Metadata []resource.MetadataField `json:"metadata"`
  24. }
  25. type imageWithConfigAsLayer struct {
  26. v1.Image
  27. }
  28. func (i imageWithConfigAsLayer) LayerByDigest(h v1.Hash) (v1.Layer, error) {
  29. // Support returning the ConfigFile when asked for its hash.
  30. if cfgName, err := i.ConfigName(); err != nil {
  31. return nil, err
  32. } else if cfgName == h {
  33. return partial.ConfigLayer(i)
  34. }
  35. return i.Image.LayerByDigest(h)
  36. }
  37. func main() {
  38. logrus.SetOutput(os.Stderr)
  39. logrus.SetFormatter(&logrus.TextFormatter{
  40. ForceColors: true,
  41. })
  42. color.NoColor = false
  43. var req OutRequest
  44. decoder := json.NewDecoder(os.Stdin)
  45. decoder.DisallowUnknownFields()
  46. err := decoder.Decode(&req)
  47. if err != nil {
  48. logrus.Errorf("invalid payload: %s", err)
  49. os.Exit(1)
  50. return
  51. }
  52. if req.Source.Debug {
  53. logrus.SetLevel(logrus.DebugLevel)
  54. }
  55. if len(os.Args) < 2 {
  56. logrus.Errorf("destination path not specified")
  57. os.Exit(1)
  58. return
  59. }
  60. src := os.Args[1]
  61. logrus.Warnln("'put' is experimental, untested, and subject to change!")
  62. ref := req.Source.Repository + ":" + req.Source.Tag()
  63. n, err := name.ParseReference(ref, name.WeakValidation)
  64. if err != nil {
  65. logrus.Errorf("could not resolve repository/tag reference: %s", err)
  66. os.Exit(1)
  67. return
  68. }
  69. imagePath := filepath.Join(src, req.Params.Image)
  70. img, err := tarball.ImageFromPath(imagePath, nil)
  71. if err != nil {
  72. logrus.Errorf("could not load image from path '%s': %s", req.Params.Image, err)
  73. os.Exit(1)
  74. return
  75. }
  76. img = imageWithConfigAsLayer{
  77. Image: img,
  78. }
  79. digest, err := img.Digest()
  80. if err != nil {
  81. logrus.Errorf("failed to get image digest: %s", err)
  82. os.Exit(1)
  83. return
  84. }
  85. logrus.Infof("pushing %s to %s", digest, ref)
  86. auth := &authn.Basic{
  87. Username: req.Source.Username,
  88. Password: req.Source.Password,
  89. }
  90. err = remote.Write(n, img, auth, http.DefaultTransport, remote.WriteOptions{})
  91. if err != nil {
  92. logrus.Errorf("failed to upload image: %s", err)
  93. os.Exit(1)
  94. return
  95. }
  96. logrus.Info("pushed")
  97. json.NewEncoder(os.Stdout).Encode(OutResponse{
  98. Version: resource.Version{
  99. Digest: digest.String(),
  100. },
  101. Metadata: []resource.MetadataField{},
  102. })
  103. }