Skip to content

Commit

Permalink
Merge pull request opencontainers#5 from jdolitsky/add-unit-tests
Browse files Browse the repository at this point in the history
push real contents test
  • Loading branch information
jdolitsky authored Dec 27, 2018
2 parents 0f6b55a + a989514 commit 26918fc
Show file tree
Hide file tree
Showing 22 changed files with 1,465 additions and 224 deletions.
5 changes: 5 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
name = "github.com/opencontainers/image-spec"
version = "1.0.1"

# Test dependency
[[constraint]]
name = "github.com/docker/distribution"
version = "2.7.0"

# These overrides below necessary for using docker/distribution as a test dependency
[[override]]
name = "github.com/yvasiyarov/newrelic_platform_go"
Expand Down
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ fix-deps:

.PHONY: test
test:
rm -rf .test/ && mkdir .test/
CGO_ENABLED=0 go test -v -covermode=atomic -coverprofile=coverage.out github.com/shizhMSFT/oras/pkg/oras
go tool cover -html=coverage.out -o=coverage.html

Expand Down
19 changes: 10 additions & 9 deletions pkg/oras/errors.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package oras

import "errors"

// Common errors
var (
ErrNotFound = errors.New("not_found")
ErrResolverUndefined = errors.New("resolver_undefined")
)
package oras

import "errors"

// Common errors
var (
ErrNotFound = errors.New("not_found")
ErrResolverUndefined = errors.New("resolver_undefined")
ErrEmptyContents = errors.New("empty_contents")
)
160 changes: 160 additions & 0 deletions pkg/oras/oras_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package oras

import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"

"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
"github.com/docker/distribution/configuration"
"github.com/docker/distribution/registry"
_ "github.com/docker/distribution/registry/storage/driver/inmemory"
"github.com/phayes/freeport"
"github.com/stretchr/testify/suite"
)

var (
testTarball = "../../testdata/charts/chartmuseum-1.8.2.tgz"
testDir = "../../testdata/charts/chartmuseum"
testDirFiles = []string{
"Chart.yaml",
"values.yaml",
"README.md",
"templates/_helpers.tpl",
"templates/NOTES.txt",
"templates/service.yaml",
".helmignore",
}
)

type ORASTestSuite struct {
suite.Suite
DockerRegistryHost string
}

func newContext() context.Context {
return context.Background()
}

func newResolver() remotes.Resolver {
return docker.NewResolver(docker.ResolverOptions{})
}

// Start Docker registry
func (suite *ORASTestSuite) SetupSuite() {
config := &configuration.Configuration{}
port, err := freeport.GetFreePort()
if err != nil {
suite.Nil(err, "no error finding free port for test registry")
}
suite.DockerRegistryHost = fmt.Sprintf("localhost:%d", port)
config.HTTP.Addr = fmt.Sprintf(":%d", port)
config.HTTP.DrainTimeout = time.Duration(10) * time.Second
config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}}
dockerRegistry, err := registry.NewRegistry(context.Background(), config)
suite.Nil(err, "no error finding free port for test registry")

go dockerRegistry.ListenAndServe()
}

// Push files to docker registry
func (suite *ORASTestSuite) Test_0_Push() {
var err error
var ref string
var contents map[string][]byte

err = Push(newContext(), nil, ref, contents)
suite.NotNil(err, "error pushing with empty resolver")

err = Push(newContext(), newResolver(), ref, contents)
suite.NotNil(err, "error pushing when context missing hostname")

ref = fmt.Sprintf("%s/empty:test", suite.DockerRegistryHost)
err = Push(newContext(), newResolver(), ref, contents)
suite.NotNil(ErrEmptyContents, err, "error pushing with empty contents")

// Load contents with test chart tgz (as single layer)
contents = make(map[string][]byte)
content, err := ioutil.ReadFile(testTarball)
suite.Nil(err, "no error loading test chart")
basename := filepath.Base(testTarball)
contents[basename] = content

ref = fmt.Sprintf("%s/chart-tgz:test", suite.DockerRegistryHost)
err = Push(newContext(), newResolver(), ref, contents)
suite.Nil(err, "no error pushing test chart tgz (as single layer)")

// Load contents with test chart dir (each file as layer)
contents = make(map[string][]byte)
var ff = func(pathX string, infoX os.FileInfo, errX error) error {
if !infoX.IsDir() {
filename := filepath.Join(filepath.Dir(pathX), infoX.Name())
content, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
contents[filename] = content
}
return nil
}

cwd, _ := os.Getwd()
os.Chdir(testDir)
filepath.Walk(".", ff)
os.Chdir(cwd)

ref = fmt.Sprintf("%s/chart-dir:test", suite.DockerRegistryHost)
err = Push(newContext(), newResolver(), ref, contents)
suite.Nil(err, "no error pushing test chart dir (each file as layer)")
}

// Pull files and verify contents
func (suite *ORASTestSuite) Test_1_Pull() {
var err error
var ref string
var contents map[string][]byte

contents, err = Pull(newContext(), nil, ref)
suite.NotNil(err, "error pulling with empty resolver")
suite.Nil(contents, "contents nil pulling with empty resolver")

// Pull non-existant
ref = fmt.Sprintf("%s/nonexistant:test", suite.DockerRegistryHost)
contents, err = Pull(newContext(), newResolver(), ref)
suite.NotNil(err, "error pulling non-existant ref")
suite.Nil(contents, "contents empty with error")

// Pull chart-tgz
ref = fmt.Sprintf("%s/chart-tgz:test", suite.DockerRegistryHost)
contents, err = Pull(newContext(), newResolver(), ref)
suite.Nil(err, "no error pulling chart-tgz ref")

// Verify the contents, single layer/file
content, err := ioutil.ReadFile(testTarball)
suite.Nil(err, "no error loading test chart")
suite.Equal(content, contents[filepath.Base(testTarball)], ".tgz content matches on pull")

// Pull chart-dir
ref = fmt.Sprintf("%s/chart-dir:test", suite.DockerRegistryHost)
contents, err = Pull(newContext(), newResolver(), ref)
suite.Nil(err, "no error pulling chart-dir ref")

// Verify the contents, multiple layers/files
cwd, _ := os.Getwd()
os.Chdir(testDir)
for _, filename := range testDirFiles {
content, err = ioutil.ReadFile(filename)
suite.Nil(err, fmt.Sprintf("no error loading %s", filename))
suite.Equal(content, contents[filename], fmt.Sprintf("%s content matches on pull", filename))
}
os.Chdir(cwd)
}

func TestORASTestSuite(t *testing.T) {
suite.Run(t, new(ORASTestSuite))
}
100 changes: 49 additions & 51 deletions pkg/oras/pull.go
Original file line number Diff line number Diff line change
@@ -1,51 +1,49 @@
package oras

import (
"context"

"github.com/containerd/containerd/images"
"github.com/containerd/containerd/remotes"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

// Pull pull files from the remote
func Pull(ctx context.Context, resolver remotes.Resolver, ref string) (map[string][]byte, error) {
if resolver == nil {
return nil, ErrResolverUndefined
}

_, desc, err := resolver.Resolve(ctx, ref)
if err != nil {
return nil, err
}
fetcher, err := resolver.Fetcher(ctx, ref)
if err != nil {
return nil, err
}

var layers []ocispec.Descriptor
picker := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
if desc.MediaType == ocispec.MediaTypeImageLayer {
layers = append(layers, desc)
}
return nil, nil
})
store := NewMemoryStore()
handlers := images.Handlers(store.FetchHandler(fetcher), picker, images.ChildrenHandler(store))
if err := images.Dispatch(ctx, handlers, desc); err != nil {
return nil, err
}

res := make(map[string][]byte)
for _, layer := range layers {
if content, ok := store.Get(layer); ok {
if title, ok := layer.Annotations[ocispec.AnnotationTitle]; ok && len(title) > 0 {
res[title] = content
} else {
res[layer.Digest.Encoded()] = content
}
}
}

return res, nil
}
package oras

import (
"context"

"github.com/containerd/containerd/images"
"github.com/containerd/containerd/remotes"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

// Pull pull files from the remote
func Pull(ctx context.Context, resolver remotes.Resolver, ref string) (map[string][]byte, error) {
if resolver == nil {
return nil, ErrResolverUndefined
}

_, desc, err := resolver.Resolve(ctx, ref)
if err != nil {
return nil, err
}
fetcher, err := resolver.Fetcher(ctx, ref)
if err != nil {
return nil, err
}

var layers []ocispec.Descriptor
picker := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
if desc.MediaType == ocispec.MediaTypeImageLayer {
layers = append(layers, desc)
}
return nil, nil
})
store := NewMemoryStore()
handlers := images.Handlers(store.FetchHandler(fetcher), picker, images.ChildrenHandler(store))
if err := images.Dispatch(ctx, handlers, desc); err != nil {
return nil, err
}

res := make(map[string][]byte)
for _, layer := range layers {
if content, ok := store.Get(layer); ok {
if title, ok := layer.Annotations[ocispec.AnnotationTitle]; ok && len(title) > 0 {
res[title] = content
}
}
}

return res, nil
}
4 changes: 4 additions & 0 deletions pkg/oras/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ func Push(ctx context.Context, resolver remotes.Resolver, ref string, contents m
return ErrResolverUndefined
}

if contents == nil {
return ErrEmptyContents
}

pusher, err := resolver.Pusher(ctx, ref)
if err != nil {
return err
Expand Down
64 changes: 0 additions & 64 deletions pkg/oras/push_test.go

This file was deleted.

Loading

0 comments on commit 26918fc

Please sign in to comment.