Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions internal/oci/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,38 +94,42 @@ func DescriptorForEnvFile(path string, content []byte) v1.Descriptor {
}
}

func PushManifest(ctx context.Context, resolver remotes.Resolver, named reference.Named, layers []v1.Descriptor, ociVersion api.OCIVersion) (v1.Descriptor, error) {
// PushManifest pushes the manifest for a Compose OCI artifact. The returned
// bool reports whether the push fell back from OCI 1.1 to OCI 1.0; it is
// only meaningful when err == nil.
func PushManifest(ctx context.Context, resolver remotes.Resolver, named reference.Named, layers []v1.Descriptor, ociVersion api.OCIVersion) (v1.Descriptor, bool, error) {
// Check if we need an extra empty layer for the manifest config
if ociVersion == api.OCIVersion1_1 || ociVersion == "" {
err := push(ctx, resolver, named, v1.DescriptorEmptyJSON)
if err != nil {
return v1.Descriptor{}, err
return v1.Descriptor{}, false, err
}
}
// prepare to push the manifest by pushing the layers
layerDescriptors := make([]v1.Descriptor, len(layers))
for i := range layers {
layerDescriptors[i] = layers[i]
if err := push(ctx, resolver, named, layers[i]); err != nil {
return v1.Descriptor{}, err
return v1.Descriptor{}, false, err
}
}

if ociVersion != "" {
// if a version was explicitly specified, use it
return createAndPushManifest(ctx, resolver, named, layerDescriptors, ociVersion)
descriptor, err := createAndPushManifest(ctx, resolver, named, layerDescriptors, ociVersion)
return descriptor, false, err
}

// try to push in the OCI 1.1 format but fallback to OCI 1.0 on 4xx errors
// (other than auth) since it's most likely the result of the registry not
// having support
// (other than auth) since it's most likely the result of the registry
// rejecting the OCI 1.1 request
descriptor, err := createAndPushManifest(ctx, resolver, named, layerDescriptors, api.OCIVersion1_1)
var pushErr pusherrors.ErrUnexpectedStatus
if errors.As(err, &pushErr) && isNonAuthClientError(pushErr.StatusCode) {
// TODO(milas): show a warning here (won't work with logrus)
return createAndPushManifest(ctx, resolver, named, layerDescriptors, api.OCIVersion1_0)
descriptor, err = createAndPushManifest(ctx, resolver, named, layerDescriptors, api.OCIVersion1_0)
return descriptor, true, err
}
return descriptor, err
return descriptor, false, err
}

func push(ctx context.Context, resolver remotes.Resolver, ref reference.Named, descriptor v1.Descriptor) error {
Expand Down
26 changes: 16 additions & 10 deletions pkg/compose/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,26 +91,32 @@ func (s *composeService) publish(ctx context.Context, project *types.Project, re
fmt.Println(string(indent))
}
}
didFallback := false
if !s.dryRun {
err = s.pushComposeArtifact(ctx, project, repository, layers, options)
didFallback, err = s.pushComposeArtifact(ctx, project, repository, layers, options)
if err != nil {
return err
}
}
text, status := "published", api.Done
if didFallback {
text, status = "published (registry rejected OCI 1.1; fell back to OCI 1.0)", api.Warning
}
s.events.On(api.Resource{
ID: repository,
Text: "published",
Status: api.Done,
Text: text,
Status: status,
})
return nil
}

// pushComposeArtifact pushes the compose artifact manifest to the repository,
// and the application image index when publishing a full application
func (s *composeService) pushComposeArtifact(ctx context.Context, project *types.Project, repository string, layers []v1.Descriptor, options api.PublishOptions) error {
// and the application image index when publishing a full application. The
// returned bool reports whether the push fell back from OCI 1.1 to OCI 1.0.
func (s *composeService) pushComposeArtifact(ctx context.Context, project *types.Project, repository string, layers []v1.Descriptor, options api.PublishOptions) (bool, error) {
named, err := reference.ParseDockerRef(repository)
if err != nil {
return err
return false, err
}

var insecureRegistries []string
Expand All @@ -120,20 +126,20 @@ func (s *composeService) pushComposeArtifact(ctx context.Context, project *types

resolver := oci.NewResolver(s.configFile(), desktop.ProxyTransportFor(ctx, s.apiClient()), insecureRegistries...)

descriptor, err := oci.PushManifest(ctx, resolver, named, layers, options.OCIVersion)
descriptor, didFallback, err := oci.PushManifest(ctx, resolver, named, layers, options.OCIVersion)
if err != nil {
s.events.On(api.Resource{
ID: repository,
Text: "publishing",
Status: api.Error,
})
return err
return false, err
}

if options.Application {
return pushApplicationIndex(ctx, resolver, named, descriptor, project)
return didFallback, pushApplicationIndex(ctx, resolver, named, descriptor, project)
}
return nil
return didFallback, nil
}

// pushApplicationIndex pushes an image index referencing every service image,
Expand Down