Skip to content

Commit

Permalink
support dry run
Browse files Browse the repository at this point in the history
Signed-off-by: Shiwei Zhang <shizh@microsoft.com>
  • Loading branch information
shizhMSFT committed Mar 29, 2021
1 parent 0e3c1f4 commit bfde944
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 1 deletion.
12 changes: 11 additions & 1 deletion cmd/oras/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path/filepath"

iresolver "github.com/deislabs/oras/internal/resolver"
"github.com/deislabs/oras/pkg/content"
ctxo "github.com/deislabs/oras/pkg/context"
"github.com/deislabs/oras/pkg/oras"
Expand All @@ -33,6 +34,7 @@ type pushOptions struct {
artifactRefs []string
pathValidationDisabled bool
verbose bool
dryRun bool

debug bool
configs []string
Expand Down Expand Up @@ -88,6 +90,7 @@ Example - Push file to the HTTP registry:
cmd.Flags().StringVarP(&opts.password, "password", "p", "", "registry password")
cmd.Flags().BoolVarP(&opts.insecure, "insecure", "", false, "allow connections to SSL registry without certs")
cmd.Flags().BoolVarP(&opts.plainHTTP, "plain-http", "", false, "use plain http and not https")
cmd.Flags().BoolVarP(&opts.dryRun, "dry-run", "", false, "push to a dummy registry instead of the real remote")
return cmd
}

Expand All @@ -99,9 +102,16 @@ func runPush(opts pushOptions) error {
ctx = ctxo.WithLoggerDiscarded(ctx)
}

// specify resolver
var resolver remotes.Resolver
if opts.dryRun {
resolver = iresolver.Dummy()
} else {
resolver = newResolver(opts.username, opts.password, opts.insecure, opts.plainHTTP, opts.configs...)
}

// bake artifact
var pushOpts []oras.PushOpt
resolver := newResolver(opts.username, opts.password, opts.insecure, opts.plainHTTP, opts.configs...)
if opts.artifactType != "" {
manifests, err := loadReferences(ctx, resolver, opts.artifactRefs)
if err != nil {
Expand Down
103 changes: 103 additions & 0 deletions internal/resolver/dummy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package resolver

import (
"context"
"io"
"time"

"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/remotes"
artifactspec "github.com/notaryproject/artifacts/specs-go/v2"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)

// dummyResolver is a dummy resolver, which resolves nothing.
// It accepts any pushes but ignores them.
type dummyResolver struct{}

var dummyResolverInstance = &dummyResolver{}

// Dummy creates a new dummy resolver
func Dummy() remotes.Resolver {
return dummyResolverInstance
}

func (r *dummyResolver) Resolve(ctx context.Context, ref string) (name string, desc ocispec.Descriptor, err error) {
return "", ocispec.Descriptor{}, errors.Wrap(errdefs.ErrNotFound, "dummy resolver")
}

func (r *dummyResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
return remotes.FetcherFunc(func(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) {
return nil, errors.Wrap(errdefs.ErrNotFound, "dummy resolver")
}), nil
}

// Pusher returns a new pusher for the provided reference
func (r *dummyResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) {
return remotes.PusherFunc(func(ctx context.Context, desc ocispec.Descriptor) (content.Writer, error) {
now := time.Now()
return &dummyWriter{
actual: digest.Canonical.Digester(),
status: content.Status{
Ref: ref,
Total: desc.Size,
Expected: desc.Digest,
StartedAt: now,
UpdatedAt: now,
},
}, nil
}), nil
}

// Discoverer returns a new discoverer for the provided reference
func (r *dummyResolver) Discoverer(ctx context.Context, ref string) (remotes.Discoverer, error) {
return remotes.DiscovererFunc(func(ctx context.Context, desc ocispec.Descriptor, artifactType string) ([]artifactspec.Artifact, error) {
return nil, errors.Wrap(errdefs.ErrNotFound, "dummy resolver")
}), nil
}

type dummyWriter struct {
actual digest.Digester
status content.Status
}

func (dw *dummyWriter) Write(p []byte) (n int, err error) {
n, err = dw.actual.Hash().Write(p)
dw.status.Offset += int64(n)
dw.status.UpdatedAt = time.Now()
return
}

func (dw *dummyWriter) Close() error {
return nil
}

func (dw *dummyWriter) Status() (content.Status, error) {
return dw.status, nil
}

func (dw *dummyWriter) Digest() digest.Digest {
return dw.status.Expected
}

func (dw *dummyWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error {
if size > 0 && size != dw.status.Offset {
return errors.Errorf("unexpected size %d, expected %d", dw.status.Offset, size)
}
if expected == "" {
expected = dw.status.Expected
}

actual := dw.actual.Digest()
if actual != expected {
return errors.Errorf("got digest %s, expected %s", actual, expected)
}
return nil
}

func (dw *dummyWriter) Truncate(size int64) error {
return errors.New("cannot truncate dummy upload")
}

0 comments on commit bfde944

Please sign in to comment.