github.com/octohelm/wagon@v0.0.0-20240308040401-88662650dc0b/pkg/engine/plan/task/container_push.go (about)

     1  package task
     2  
     3  import (
     4  	"context"
     5  	"dagger.io/dagger"
     6  	"encoding/json"
     7  
     8  	"github.com/octohelm/wagon/pkg/engine/daggerutil"
     9  	"github.com/octohelm/wagon/pkg/engine/plan"
    10  	"github.com/octohelm/wagon/pkg/engine/plan/task/core"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  func init() {
    15  	core.DefaultFactory.Register(&Push{})
    16  }
    17  
    18  type Push struct {
    19  	Pusher
    20  }
    21  
    22  func (Push) OneOf() []any {
    23  	return []any{
    24  		&PushImage{},
    25  		&PushManifests{},
    26  	}
    27  }
    28  
    29  func (p *Push) UnmarshalJSON(bytes []byte) error {
    30  	ret := struct {
    31  		Type string `json:"type"`
    32  	}{}
    33  
    34  	if err := json.Unmarshal(bytes, &ret); err != nil {
    35  		return err
    36  	}
    37  
    38  	if ret.Type == "manifests" {
    39  		m := &PushManifests{}
    40  		if err := json.Unmarshal(bytes, m); err != nil {
    41  			return err
    42  		}
    43  		p.Pusher = m
    44  	}
    45  
    46  	m := &PushImage{}
    47  	if err := json.Unmarshal(bytes, m); err != nil {
    48  		return err
    49  	}
    50  	p.Pusher = m
    51  	return nil
    52  }
    53  
    54  type Pusher interface {
    55  	Do(ctx context.Context) error
    56  }
    57  
    58  type PushImage struct {
    59  	core.Task
    60  
    61  	Dest string `json:"dest"`
    62  	Type string `json:"type" enum:"image"`
    63  
    64  	Input    core.FS          `json:"input"`
    65  	Config   core.ImageConfig `json:"config"`
    66  	Platform string           `json:"platform,omitempty"`
    67  	Format   string           `json:"format,omitempty" enum:"docker,oci" default:"docker"`
    68  
    69  	Auth *core.Auth `json:"auth,omitempty"`
    70  
    71  	Result string `json:"-" wagon:"generated,name=result"`
    72  }
    73  
    74  func (input *PushImage) Do(ctx context.Context) error {
    75  	return daggerutil.Do(ctx, func(c *dagger.Client) error {
    76  		id := input.Input.DirectoryID()
    77  		if id == "" {
    78  			return errors.Errorf("missing fs")
    79  		}
    80  
    81  		dir := c.LoadDirectoryFromID(id)
    82  
    83  		ctr := input.Config.ApplyTo(c.Container(dagger.ContainerOpts{
    84  			Platform: dagger.Platform(input.Platform),
    85  		}).WithRootfs(dir))
    86  
    87  		ct := c.Container()
    88  
    89  		ct = plan.RegistryAuthStoreContext.From(ctx).ApplyTo(ctx, ct)
    90  		if a := input.Auth; a != nil {
    91  			ct = a.ApplyTo(ctx, ct, input.Dest)
    92  		}
    93  
    94  		// prepare before push
    95  		_, err := ctr.Rootfs().Entries(ctx)
    96  		if err != nil {
    97  			return err
    98  		}
    99  
   100  		ret, err := ct.Publish(ctx, input.Dest, dagger.ContainerPublishOpts{
   101  			PlatformVariants: []*dagger.Container{
   102  				ctr,
   103  			},
   104  			MediaTypes: Format(input.Format).MediaTypes(),
   105  		})
   106  		if err != nil {
   107  			return err
   108  		}
   109  		input.Result = ret
   110  		return nil
   111  	})
   112  
   113  }
   114  
   115  type PushManifests struct {
   116  	core.Task
   117  
   118  	Dest   string `json:"dest"`
   119  	Type   string `json:"type" enum:"manifests"`
   120  	Format string `json:"format,omitempty" enum:"docker,oci" default:"docker"`
   121  
   122  	Inputs map[string]struct {
   123  		Input  core.FS          `json:"input"`
   124  		Config core.ImageConfig `json:"config"`
   125  	} `json:"inputs"`
   126  
   127  	Auth *core.Auth `json:"auth,omitempty"`
   128  
   129  	Result string `json:"-" wagon:"generated,name=result"`
   130  }
   131  
   132  func (input *PushManifests) Do(ctx context.Context) error {
   133  	return daggerutil.Do(ctx, func(c *dagger.Client) error {
   134  		cts := make([]*dagger.Container, 0, len(input.Inputs))
   135  
   136  		for platform := range input.Inputs {
   137  			img := input.Inputs[platform]
   138  			id := img.Input.DirectoryID()
   139  			if id == "" {
   140  				return errors.Errorf("missing fs for %s", platform)
   141  			}
   142  
   143  			dir := c.LoadDirectoryFromID(id)
   144  
   145  			ctr := img.Config.ApplyTo(c.Container(dagger.ContainerOpts{Platform: dagger.Platform(platform)}).WithRootfs(dir))
   146  
   147  			// prepare before push
   148  			_, err := ctr.Rootfs().Entries(ctx)
   149  			if err != nil {
   150  				return err
   151  			}
   152  
   153  			cts = append(cts, ctr)
   154  		}
   155  
   156  		ct := plan.RegistryAuthStoreContext.From(ctx).ApplyTo(ctx, c.Container())
   157  		if a := input.Auth; a != nil {
   158  			ct = a.ApplyTo(ctx, ct, input.Dest)
   159  		}
   160  
   161  		ret, err := ct.Publish(ctx, input.Dest, dagger.ContainerPublishOpts{
   162  			PlatformVariants: cts,
   163  			MediaTypes:       Format(input.Format).MediaTypes(),
   164  		})
   165  		if err != nil {
   166  			return err
   167  		}
   168  		input.Result = ret
   169  		return nil
   170  	})
   171  }
   172  
   173  type Format string
   174  
   175  func (f Format) MediaTypes() dagger.ImageMediaTypes {
   176  	if f == "oci" {
   177  		return dagger.Ocimediatypes
   178  	}
   179  	return dagger.Dockermediatypes
   180  }