mirror of https://github.com/docker/compose.git
introduce --resolve-image-digests for publish to seal service images by digest
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
5661fd1bfe
commit
6727908803
|
@ -25,11 +25,16 @@ import (
|
|||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
type publishOptions struct {
|
||||
*ProjectOptions
|
||||
resolveImageDigests bool
|
||||
}
|
||||
|
||||
func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
opts := pushOptions{
|
||||
opts := publishOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
publishCmd := &cobra.Command{
|
||||
cmd := &cobra.Command{
|
||||
Use: "publish [OPTIONS] [REPOSITORY]",
|
||||
Short: "Publish compose application",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
|
@ -37,14 +42,18 @@ func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
|
|||
}),
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
return publishCmd
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&opts.resolveImageDigests, "resolve-image-digests", false, "Pin image tags to digests.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pushOptions, repository string) error {
|
||||
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts publishOptions, repository string) error {
|
||||
project, err := opts.ToProject(dockerCli, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return backend.Publish(ctx, project, repository, api.PublishOptions{})
|
||||
return backend.Publish(ctx, project, repository, api.PublishOptions{
|
||||
ResolveImageDigests: opts.resolveImageDigests,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@ Publish compose application
|
|||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:-----|:--------|:--------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:-----|:--------|:--------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--resolve-image-digests` | | | Pin image tags to digests. |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
|
|
@ -4,6 +4,17 @@ long: Publish compose application
|
|||
usage: docker compose alpha publish [OPTIONS] [REPOSITORY]
|
||||
pname: docker compose alpha
|
||||
plink: docker_compose_alpha.yaml
|
||||
options:
|
||||
- option: resolve-image-digests
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Pin image tags to digests.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
|
|
@ -363,6 +363,7 @@ type PortOptions struct {
|
|||
|
||||
// PublishOptions group options of the Publish API
|
||||
type PublishOptions struct {
|
||||
ResolveImageDigests bool
|
||||
}
|
||||
|
||||
func (e Event) String() string {
|
||||
|
|
|
@ -63,37 +63,24 @@ func (s *composeService) publish(ctx context.Context, project *types.Project, re
|
|||
return err
|
||||
}
|
||||
|
||||
w.Event(progress.Event{
|
||||
ID: file,
|
||||
Text: "publishing",
|
||||
Status: progress.Working,
|
||||
})
|
||||
layer := v1.Descriptor{
|
||||
MediaType: "application/vnd.docker.compose.file+yaml",
|
||||
Digest: digest.FromString(string(f)),
|
||||
Size: int64(len(f)),
|
||||
Annotations: map[string]string{
|
||||
"com.docker.compose.version": api.ComposeVersion,
|
||||
"com.docker.compose.file": filepath.Base(file),
|
||||
},
|
||||
layer, err := s.pushComposeFile(ctx, file, f, resolver, named)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
layers = append(layers, layer)
|
||||
err = resolver.Push(ctx, named, layer, f)
|
||||
if err != nil {
|
||||
w.Event(progress.Event{
|
||||
ID: file,
|
||||
Text: "publishing",
|
||||
Status: progress.Error,
|
||||
})
|
||||
}
|
||||
|
||||
if options.ResolveImageDigests {
|
||||
yaml, err := s.generateImageDigestsOverride(ctx, project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Event(progress.Event{
|
||||
ID: file,
|
||||
Text: "published",
|
||||
Status: progress.Done,
|
||||
})
|
||||
layer, err := s.pushComposeFile(ctx, "image-digests.yaml", yaml, resolver, named)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
layers = append(layers, layer)
|
||||
}
|
||||
|
||||
emptyConfig, err := json.Marshal(v1.ImageConfig{})
|
||||
|
@ -157,3 +144,61 @@ func (s *composeService) publish(ctx context.Context, project *types.Project, re
|
|||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *composeService) generateImageDigestsOverride(ctx context.Context, project *types.Project) ([]byte, error) {
|
||||
project.ApplyProfiles([]string{"*"})
|
||||
err := project.ResolveImages(func(named reference.Named) (digest.Digest, error) {
|
||||
auth, err := encodedAuth(named, s.configFile())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
inspect, err := s.apiClient().DistributionInspect(ctx, named.String(), auth)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return inspect.Descriptor.Digest, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
override := types.Project{}
|
||||
for _, service := range project.Services {
|
||||
override.Services = append(override.Services, types.ServiceConfig{
|
||||
Name: service.Name,
|
||||
Image: service.Image,
|
||||
})
|
||||
}
|
||||
return override.MarshalYAML()
|
||||
}
|
||||
|
||||
func (s *composeService) pushComposeFile(ctx context.Context, file string, content []byte, resolver *imagetools.Resolver, named reference.Named) (v1.Descriptor, error) {
|
||||
w := progress.ContextWriter(ctx)
|
||||
w.Event(progress.Event{
|
||||
ID: file,
|
||||
Text: "publishing",
|
||||
Status: progress.Working,
|
||||
})
|
||||
layer := v1.Descriptor{
|
||||
MediaType: "application/vnd.docker.compose.file+yaml",
|
||||
Digest: digest.FromString(string(content)),
|
||||
Size: int64(len(content)),
|
||||
Annotations: map[string]string{
|
||||
"com.docker.compose.version": api.ComposeVersion,
|
||||
"com.docker.compose.file": filepath.Base(file),
|
||||
},
|
||||
}
|
||||
err := resolver.Push(ctx, named, layer, content)
|
||||
w.Event(progress.Event{
|
||||
ID: file,
|
||||
Text: "published",
|
||||
Status: statusFor(err),
|
||||
})
|
||||
return layer, err
|
||||
}
|
||||
|
||||
func statusFor(err error) progress.EventStatus {
|
||||
if err != nil {
|
||||
return progress.Error
|
||||
}
|
||||
return progress.Done
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue