Browse Source

introduce push --repository

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 2 years ago
parent
commit
49d1bc7524
5 changed files with 79 additions and 7 deletions
  1. 3 0
      cmd/compose/push.go
  2. 2 1
      go.mod
  3. 4 0
      go.sum
  4. 1 0
      pkg/api/api.go
  5. 69 6
      pkg/compose/push.go

+ 3 - 0
cmd/compose/push.go

@@ -31,6 +31,7 @@ type pushOptions struct {
 	IncludeDeps    bool
 	Ignorefailures bool
 	Quiet          bool
+	Repository     string
 }
 
 func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
@@ -48,6 +49,7 @@ func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
 	pushCmd.Flags().BoolVar(&opts.Ignorefailures, "ignore-push-failures", false, "Push what it can and ignores images with push failures")
 	pushCmd.Flags().BoolVar(&opts.IncludeDeps, "include-deps", false, "Also push images of services declared as dependencies")
 	pushCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Push without printing progress information")
+	pushCmd.Flags().StringVarP(&opts.Repository, "repository", "r", "", "Also publish the compose application in repository")
 
 	return pushCmd
 }
@@ -68,5 +70,6 @@ func runPush(ctx context.Context, backend api.Service, opts pushOptions, service
 	return backend.Push(ctx, project, api.PushOptions{
 		IgnoreFailures: opts.Ignorefailures,
 		Quiet:          opts.Quiet,
+		Repository:     opts.Repository,
 	})
 }

+ 2 - 1
go.mod

@@ -29,7 +29,7 @@ require (
 	github.com/moby/term v0.5.0
 	github.com/morikuni/aec v1.0.0
 	github.com/opencontainers/go-digest v1.0.0
-	github.com/opencontainers/image-spec v1.1.0-rc3
+	github.com/opencontainers/image-spec v1.1.0-rc4
 	github.com/pkg/errors v0.9.1
 	github.com/sirupsen/logrus v1.9.3
 	github.com/spf13/cobra v1.7.0
@@ -183,6 +183,7 @@ require (
 	k8s.io/klog/v2 v2.90.1 // indirect
 	k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
 	k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
+	oras.land/oras-go/v2 v2.2.0 // indirect
 	sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
 	sigs.k8s.io/yaml v1.3.0 // indirect

+ 4 - 0
go.sum

@@ -513,6 +513,8 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I
 github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
 github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
 github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
+github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
+github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
 github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk=
 github.com/opencontainers/runc v1.1.7/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50=
 github.com/opencontainers/runtime-spec v1.1.0-rc.2 h1:ucBtEms2tamYYW/SvGpvq9yUN0NEVL6oyLEwDcTSrk8=
@@ -1110,6 +1112,8 @@ k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+O
 k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=
 k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk=
 k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+oras.land/oras-go/v2 v2.2.0 h1:E1fqITD56Eg5neZbxBtAdZVgDHD6wBabJo6xESTcQyo=
+oras.land/oras-go/v2 v2.2.0/go.mod h1:pXjn0+KfarspMHHNR3A56j3tgvr+mxArHuI8qVn59v8=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

+ 1 - 0
pkg/api/api.go

@@ -260,6 +260,7 @@ type ConfigOptions struct {
 // PushOptions group options of the Push API
 type PushOptions struct {
 	Quiet          bool
+	Repository     string
 	IgnoreFailures bool
 }
 

+ 69 - 6
pkg/compose/push.go

@@ -17,6 +17,7 @@
 package compose
 
 import (
+	"bytes"
 	"context"
 	"encoding/base64"
 	"encoding/json"
@@ -26,14 +27,17 @@ import (
 	"github.com/compose-spec/compose-go/types"
 	"github.com/distribution/distribution/v3/reference"
 	"github.com/docker/buildx/driver"
+	"github.com/docker/compose/v2/pkg/api"
+	"github.com/docker/compose/v2/pkg/progress"
 	moby "github.com/docker/docker/api/types"
 	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/registry"
+	"github.com/opencontainers/go-digest"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
 	"golang.org/x/sync/errgroup"
-
-	"github.com/docker/compose/v2/pkg/api"
-	"github.com/docker/compose/v2/pkg/progress"
+	"oras.land/oras-go/v2/content"
+	"oras.land/oras-go/v2/registry/remote"
 )
 
 func (s *composeService) Push(ctx context.Context, project *types.Project, options api.PushOptions) error {
@@ -45,8 +49,8 @@ func (s *composeService) Push(ctx context.Context, project *types.Project, optio
 	}, s.stdinfo(), "Pushing")
 }
 
-func (s *composeService) push(ctx context.Context, project *types.Project, options api.PushOptions) error {
-	eg, ctx := errgroup.WithContext(ctx)
+func (s *composeService) push(upctx context.Context, project *types.Project, options api.PushOptions) error {
+	eg, ctx := errgroup.WithContext(upctx)
 	eg.SetLimit(s.maxConcurrency)
 
 	info, err := s.apiClient().Info(ctx)
@@ -79,7 +83,66 @@ func (s *composeService) push(ctx context.Context, project *types.Project, optio
 			return nil
 		})
 	}
-	return eg.Wait()
+
+	err = eg.Wait()
+	if err != nil {
+		return err
+	}
+	ctx = upctx
+
+	if options.Repository != "" {
+		repository, err := remote.NewRepository(options.Repository)
+		if err != nil {
+			return err
+		}
+
+		yaml, err := project.MarshalYAML()
+		if err != nil {
+			return err
+		}
+		manifests := []v1.Descriptor{
+			{
+				MediaType:    "application/vnd.oci.artifact.manifest.v1+json",
+				Digest:       digest.FromBytes(yaml),
+				Size:         int64(len(yaml)),
+				Data:         yaml,
+				ArtifactType: "application/vnd.docker.compose.yaml",
+			},
+		}
+		for _, service := range project.Services {
+			inspected, _, err := s.dockerCli.Client().ImageInspectWithRaw(ctx, service.Image)
+			if err != nil {
+				return err
+			}
+			manifests = append(manifests, v1.Descriptor{
+				MediaType: v1.MediaTypeImageIndex,
+				Digest:    digest.Digest(inspected.RepoDigests[0]),
+				Size:      inspected.Size,
+				Annotations: map[string]string{
+					"com.docker.compose.service": service.Name,
+				},
+			})
+		}
+
+		manifest := v1.Index{
+			MediaType: v1.MediaTypeImageIndex,
+			Manifests: manifests,
+			Annotations: map[string]string{
+				"com.docker.compose": api.ComposeVersion,
+			},
+		}
+		manifestContent, err := json.Marshal(manifest)
+		if err != nil {
+			return err
+		}
+		manifestDescriptor := content.NewDescriptorFromBytes(v1.MediaTypeImageIndex, manifestContent)
+
+		err = repository.Push(ctx, manifestDescriptor, bytes.NewReader(manifestContent))
+		if err != nil {
+			return err
+		}
+	}
+	return nil
 }
 
 func (s *composeService) pushServiceImage(ctx context.Context, service types.ServiceConfig, info moby.Info, configFile driver.Auth, w progress.Writer, quietPush bool) error {