55 "encoding/json"
66 "fmt"
77 "io"
8+ "io/ioutil"
89
910 "github.com/docker/cli/cli"
1011 "github.com/docker/cli/cli/command"
@@ -14,13 +15,16 @@ import (
1415 "github.com/docker/distribution/manifest/schema2"
1516 "github.com/docker/distribution/reference"
1617 "github.com/docker/docker/registry"
18+
1719 "github.com/pkg/errors"
1820 "github.com/spf13/cobra"
21+ yaml "gopkg.in/yaml.v2"
1922)
2023
2124type pushOpts struct {
2225 insecure bool
2326 purge bool
27+ file bool
2428 target string
2529}
2630
@@ -42,32 +46,48 @@ type pushRequest struct {
4246 insecure bool
4347}
4448
49+ type yamlManifestList struct {
50+ Image string
51+ Manifests []yamlManifest
52+ }
53+
54+ type yamlManifest struct {
55+ Image string
56+ Platform manifestlist.PlatformSpec
57+ }
58+
4559func newPushListCommand (dockerCli command.Cli ) * cobra.Command {
4660 opts := pushOpts {}
4761
4862 cmd := & cobra.Command {
4963 Use : "push [OPTIONS] MANIFEST_LIST" ,
50- Short : "Push a manifest list to a repository" ,
64+ Short : "Push a manifest list to a repository, either after a create, or from a file " ,
5165 Args : cli .ExactArgs (1 ),
5266 RunE : func (cmd * cobra.Command , args []string ) error {
5367 opts .target = args [0 ]
5468 return runPush (dockerCli , opts )
5569 },
5670 }
57-
5871 flags := cmd .Flags ()
59- flags .BoolVarP (& opts .purge , "purge" , "p" , false , "Remove the local manifest list after push" )
60- flags .BoolVar (& opts .insecure , "insecure" , false , "Allow push to an insecure registry" )
72+ flags .BoolVarP (& opts .purge , "purge" , "p" , false , "remove the local manifests after push" )
73+ flags .BoolVar (& opts .insecure , "insecure" , false , "allow push to an insecure registry" )
74+ flags .BoolVar (& opts .file , "file" , false , "use a file containing the yaml representation of manifest list" )
6175 return cmd
6276}
6377
6478func runPush (dockerCli command.Cli , opts pushOpts ) error {
79+ if opts .file {
80+ return pushListFromYaml (dockerCli , opts .target , opts .insecure )
81+ }
82+
83+ return pushListFromStore (dockerCli , opts )
84+ }
6585
86+ func pushListFromStore (dockerCli command.Cli , opts pushOpts ) error {
6687 targetRef , err := normalizeReference (opts .target )
6788 if err != nil {
6889 return err
6990 }
70-
7191 manifests , err := dockerCli .ManifestStore ().GetList (targetRef )
7292 if err != nil {
7393 return err
@@ -271,3 +291,68 @@ func mountBlobs(ctx context.Context, client registryclient.RegistryClient, ref r
271291 }
272292 return nil
273293}
294+
295+ func pushListFromYaml (dockerCli command.Cli , file string , insecure bool ) error {
296+ yamlInput , err := getYamlManifestList (file )
297+ if err != nil {
298+ return err
299+ }
300+ if len (yamlInput .Manifests ) == 0 {
301+ return errors .Errorf ("no manifests specified in file input" )
302+ }
303+
304+ targetRef , err := normalizeReference (yamlInput .Image )
305+ if err != nil {
306+ return err
307+ }
308+
309+ ctx := context .Background ()
310+ var manifests []types.ImageManifest
311+ for _ , manifest := range yamlInput .Manifests {
312+ imageRef , err := normalizeReference (manifest .Image )
313+ if err != nil {
314+ return err
315+ }
316+ im , err := dockerCli .RegistryClient (insecure ).GetManifest (ctx , imageRef )
317+ if err != nil {
318+ return err
319+ }
320+ addYamlAnnotations (& im , manifest )
321+ if err := validateOSArch (im .Platform .OS , im .Platform .Architecture ); err != nil {
322+ return err
323+ }
324+ manifests = append (manifests , im )
325+ }
326+
327+ pushRequest , err := buildPushRequest (manifests , targetRef , insecure )
328+ if err != nil {
329+ return err
330+ }
331+ return pushList (ctx , dockerCli , pushRequest )
332+ }
333+
334+ func addYamlAnnotations (manifest * types.ImageManifest , ym yamlManifest ) {
335+
336+ if ym .Platform .Variant != "" {
337+ manifest .Platform .Variant = ym .Platform .Variant
338+ }
339+ if ym .Platform .OS != "" {
340+ manifest .Platform .OS = ym .Platform .OS
341+ }
342+ if ym .Platform .Architecture != "" {
343+ manifest .Platform .Architecture = ym .Platform .Architecture
344+ }
345+ if len (ym .Platform .OSFeatures ) != 0 {
346+ manifest .Platform .OSFeatures = ym .Platform .OSFeatures
347+ }
348+ }
349+
350+ func getYamlManifestList (yamlFile string ) (yamlManifestList , error ) {
351+ var yamlInput yamlManifestList
352+
353+ yamlBuf , err := ioutil .ReadFile (yamlFile )
354+ if err != nil {
355+ return yamlManifestList {}, err
356+ }
357+ return yamlInput , yaml .UnmarshalStrict (yamlBuf , & yamlInput )
358+ }
0 commit comments