update containerd library
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>docker-18.09
parent
376093d234
commit
47a00b84fe
|
@ -77,20 +77,20 @@ func (ce *CacheExporter) Finalize(ctx context.Context, cc *v1.CacheChains, targe
|
|||
return err
|
||||
}
|
||||
dgst := digest.FromBytes(dt)
|
||||
|
||||
desc := ocispec.Descriptor{
|
||||
Digest: dgst,
|
||||
Size: int64(len(dt)),
|
||||
MediaType: v1.CacheConfigMediaTypeV0,
|
||||
}
|
||||
configDone := oneOffProgress(ctx, fmt.Sprintf("writing config %s", dgst))
|
||||
buf := contentutil.NewBuffer()
|
||||
if err := content.WriteBlob(ctx, buf, dgst.String(), bytes.NewReader(dt), int64(len(dt)), dgst); err != nil {
|
||||
if err := content.WriteBlob(ctx, buf, dgst.String(), bytes.NewReader(dt), desc); err != nil {
|
||||
return configDone(errors.Wrap(err, "error writing config blob"))
|
||||
}
|
||||
configDone(nil)
|
||||
|
||||
mp.Add(dgst, buf)
|
||||
mfst.Manifests = append(mfst.Manifests, ocispec.Descriptor{
|
||||
MediaType: v1.CacheConfigMediaTypeV0,
|
||||
Size: int64(len(dt)),
|
||||
Digest: dgst,
|
||||
})
|
||||
mfst.Manifests = append(mfst.Manifests, desc)
|
||||
|
||||
dt, err = json.Marshal(mfst)
|
||||
if err != nil {
|
||||
|
@ -99,8 +99,12 @@ func (ce *CacheExporter) Finalize(ctx context.Context, cc *v1.CacheChains, targe
|
|||
dgst = digest.FromBytes(dt)
|
||||
|
||||
buf = contentutil.NewBuffer()
|
||||
desc = ocispec.Descriptor{
|
||||
Digest: dgst,
|
||||
Size: int64(len(dt)),
|
||||
}
|
||||
mfstDone := oneOffProgress(ctx, fmt.Sprintf("writing manifest %s", dgst))
|
||||
if err := content.WriteBlob(ctx, buf, dgst.String(), bytes.NewReader(dt), int64(len(dt)), dgst); err != nil {
|
||||
if err := content.WriteBlob(ctx, buf, dgst.String(), bytes.NewReader(dt), desc); err != nil {
|
||||
return mfstDone(errors.Wrap(err, "error writing manifest blob"))
|
||||
}
|
||||
mfstDone(nil)
|
||||
|
|
|
@ -73,7 +73,7 @@ func (ci *CacheImporter) Resolve(ctx context.Context, ref string) (solver.CacheM
|
|||
return nil, err
|
||||
}
|
||||
|
||||
dt, err := content.ReadBlob(ctx, b, desc.Digest)
|
||||
dt, err := content.ReadBlob(ctx, b, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ func (ci *CacheImporter) Resolve(ctx context.Context, ref string) (solver.CacheM
|
|||
return nil, err
|
||||
}
|
||||
|
||||
dt, err = content.ReadBlob(ctx, b, configDesc.Digest)
|
||||
dt, err = content.ReadBlob(ctx, b, configDesc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -497,7 +497,7 @@ func testBuildPushAndValidate(t *testing.T, sb integration.Sandbox) {
|
|||
desc, err := img.Config(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err = content.ReadBlob(ctx, img.ContentStore(), desc.Digest)
|
||||
dt, err = content.ReadBlob(ctx, img.ContentStore(), desc)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ociimg ocispec.Image
|
||||
|
@ -528,7 +528,7 @@ func testBuildPushAndValidate(t *testing.T, sb integration.Sandbox) {
|
|||
require.True(t, ociimg.History[1].EmptyLayer)
|
||||
require.False(t, ociimg.History[2].EmptyLayer)
|
||||
|
||||
dt, err = content.ReadBlob(ctx, img.ContentStore(), img.Target().Digest)
|
||||
dt, err = content.ReadBlob(ctx, img.ContentStore(), img.Target())
|
||||
require.NoError(t, err)
|
||||
|
||||
var mfst schema2.Manifest
|
||||
|
@ -538,7 +538,7 @@ func testBuildPushAndValidate(t *testing.T, sb integration.Sandbox) {
|
|||
require.Equal(t, schema2.MediaTypeManifest, mfst.MediaType)
|
||||
require.Equal(t, 2, len(mfst.Layers))
|
||||
|
||||
dt, err = content.ReadBlob(ctx, img.ContentStore(), mfst.Layers[0].Digest)
|
||||
dt, err = content.ReadBlob(ctx, img.ContentStore(), ocispec.Descriptor{Digest: mfst.Layers[0].Digest})
|
||||
require.NoError(t, err)
|
||||
|
||||
m, err := readTarToMap(dt, true)
|
||||
|
@ -561,7 +561,7 @@ func testBuildPushAndValidate(t *testing.T, sb integration.Sandbox) {
|
|||
_, ok = m["foo/sub/baz"]
|
||||
require.False(t, ok)
|
||||
|
||||
dt, err = content.ReadBlob(ctx, img.ContentStore(), mfst.Layers[1].Digest)
|
||||
dt, err = content.ReadBlob(ctx, img.ContentStore(), ocispec.Descriptor{Digest: mfst.Layers[1].Digest})
|
||||
require.NoError(t, err)
|
||||
|
||||
m, err = readTarToMap(dt, true)
|
||||
|
|
|
@ -103,16 +103,24 @@ func (ic *ImageWriter) Commit(ctx context.Context, ref cache.ImmutableRef, confi
|
|||
}
|
||||
|
||||
mfstDigest := digest.FromBytes(mfstJSON)
|
||||
mfstDesc := ocispec.Descriptor{
|
||||
Digest: mfstDigest,
|
||||
Size: int64(len(mfstJSON)),
|
||||
}
|
||||
mfstDone := oneOffProgress(ctx, "exporting manifest "+mfstDigest.String())
|
||||
|
||||
if err := content.WriteBlob(ctx, ic.opt.ContentStore, mfstDigest.String(), bytes.NewReader(mfstJSON), int64(len(mfstJSON)), mfstDigest, content.WithLabels(labels)); err != nil {
|
||||
if err := content.WriteBlob(ctx, ic.opt.ContentStore, mfstDigest.String(), bytes.NewReader(mfstJSON), mfstDesc, content.WithLabels(labels)); err != nil {
|
||||
return nil, mfstDone(errors.Wrapf(err, "error writing manifest blob %s", mfstDigest))
|
||||
}
|
||||
mfstDone(nil)
|
||||
|
||||
configDesc := ocispec.Descriptor{
|
||||
Digest: configDigest,
|
||||
Size: int64(len(config)),
|
||||
}
|
||||
configDone := oneOffProgress(ctx, "exporting config "+configDigest.String())
|
||||
|
||||
if err := content.WriteBlob(ctx, ic.opt.ContentStore, configDigest.String(), bytes.NewReader(config), int64(len(config)), configDigest); err != nil {
|
||||
if err := content.WriteBlob(ctx, ic.opt.ContentStore, configDigest.String(), bytes.NewReader(config), configDesc); err != nil {
|
||||
return nil, configDone(errors.Wrap(err, "error writing config blob"))
|
||||
}
|
||||
configDone(nil)
|
||||
|
|
|
@ -199,7 +199,7 @@ ENTRYPOINT my entrypoint
|
|||
desc, err := img.Config(ctx, ctr.ContentStore(), platforms.Default())
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := content.ReadBlob(ctx, ctr.ContentStore(), desc.Digest)
|
||||
dt, err := content.ReadBlob(ctx, ctr.ContentStore(), desc)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ociimg ocispec.Image
|
||||
|
@ -290,7 +290,7 @@ COPY foo .
|
|||
desc, err := img.Config(ctx, ctr.ContentStore(), platforms.Default())
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := content.ReadBlob(ctx, ctr.ContentStore(), desc.Digest)
|
||||
dt, err := content.ReadBlob(ctx, ctr.ContentStore(), desc)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ociimg ocispec.Image
|
||||
|
@ -789,7 +789,7 @@ ENV foo=bar
|
|||
desc, err := img.Config(ctx, client.ContentStore(), platforms.Default())
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := content.ReadBlob(ctx, client.ContentStore(), desc.Digest)
|
||||
dt, err := content.ReadBlob(ctx, client.ContentStore(), desc)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ociimg ocispec.Image
|
||||
|
@ -872,7 +872,7 @@ EXPOSE 5000
|
|||
desc, err := img.Config(ctx, client.ContentStore(), platforms.Default())
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := content.ReadBlob(ctx, client.ContentStore(), desc.Digest)
|
||||
dt, err := content.ReadBlob(ctx, client.ContentStore(), desc)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ociimg ocispec.Image
|
||||
|
@ -1052,7 +1052,7 @@ RUN ["ls"]
|
|||
desc, err := img.Config(ctx, client.ContentStore(), platforms.Default())
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := content.ReadBlob(ctx, client.ContentStore(), desc.Digest)
|
||||
dt, err := content.ReadBlob(ctx, client.ContentStore(), desc)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ociimg ocispec.Image
|
||||
|
@ -1157,7 +1157,7 @@ USER nobody
|
|||
desc, err := img.Config(ctx, client.ContentStore(), platforms.Default())
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err = content.ReadBlob(ctx, client.ContentStore(), desc.Digest)
|
||||
dt, err = content.ReadBlob(ctx, client.ContentStore(), desc)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ociimg ocispec.Image
|
||||
|
@ -1730,7 +1730,7 @@ LABEL foo=bar
|
|||
desc, err := img.Config(ctx, client.ContentStore(), platforms.Default())
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := content.ReadBlob(ctx, client.ContentStore(), desc.Digest)
|
||||
dt, err := content.ReadBlob(ctx, client.ContentStore(), desc)
|
||||
require.NoError(t, err)
|
||||
|
||||
var ociimg ocispec.Image
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
set -eu -o pipefail -x
|
||||
|
||||
iidfile=$(mktemp -t docker-iidfile.XXXXXXXXXX)
|
||||
docker build --build-arg VNDR_VERSION=15dc0965b7ba6dd78ae741e6fda33f23207033c4 --iidfile $iidfile -f ./hack/dockerfiles/vendor.Dockerfile --force-rm .
|
||||
docker build --build-arg VNDR_VERSION=1fc68ee0c852556a9ed53cbde16247033f104111 --iidfile $iidfile -f ./hack/dockerfiles/vendor.Dockerfile --force-rm .
|
||||
iid=$(cat $iidfile)
|
||||
cid=$(docker create $iid noop)
|
||||
rm -rf ./vendor
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
set -eu -o pipefail -x
|
||||
|
||||
iidfile=$(mktemp -t docker-iidfile.XXXXXXXXXX)
|
||||
docker build --build-arg VNDR_VERSION=15dc0965b7ba6dd78ae741e6fda33f23207033c4 --iidfile $iidfile -f ./hack/dockerfiles/vendor.Dockerfile --force-rm .
|
||||
docker build --build-arg VNDR_VERSION=1fc68ee0c852556a9ed53cbde16247033f104111 --iidfile $iidfile -f ./hack/dockerfiles/vendor.Dockerfile --force-rm .
|
||||
iid=$(cat $iidfile)
|
||||
diffs="$(docker run --rm $iid git status --porcelain -- vendor 2>/dev/null)"
|
||||
if [ "$diffs" ]; then
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type garbageCollectFn func(context.Context) error
|
||||
|
@ -65,28 +66,34 @@ func (c *nsContent) Abort(ctx context.Context, ref string) error {
|
|||
return c.Store.Abort(ctx, ref)
|
||||
}
|
||||
|
||||
func (c *nsContent) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) {
|
||||
func (c *nsContent) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
|
||||
ctx = namespaces.WithNamespace(ctx, c.ns)
|
||||
return c.Store.ReaderAt(ctx, dgst)
|
||||
return c.Store.ReaderAt(ctx, desc)
|
||||
}
|
||||
|
||||
func (c *nsContent) Writer(ctx context.Context, ref string, size int64, expected digest.Digest) (content.Writer, error) {
|
||||
return c.writer(ctx, ref, size, expected, 3)
|
||||
func (c *nsContent) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
|
||||
return c.writer(ctx, 3, opts...)
|
||||
}
|
||||
|
||||
func (c *nsContent) writer(ctx context.Context, ref string, size int64, expected digest.Digest, retries int) (content.Writer, error) {
|
||||
func (c *nsContent) writer(ctx context.Context, retries int, opts ...content.WriterOpt) (content.Writer, error) {
|
||||
var wOpts content.WriterOpts
|
||||
for _, opt := range opts {
|
||||
if err := opt(&wOpts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ctx = namespaces.WithNamespace(ctx, c.ns)
|
||||
w, err := c.Store.Writer(ctx, ref, size, expected)
|
||||
w, err := c.Store.Writer(ctx, opts...)
|
||||
if err != nil {
|
||||
if errdefs.IsAlreadyExists(err) && expected != "" && retries > 0 {
|
||||
if errdefs.IsAlreadyExists(err) && wOpts.Desc.Digest != "" && retries > 0 {
|
||||
_, err2 := c.Update(ctx, content.Info{
|
||||
Digest: expected,
|
||||
Digest: wOpts.Desc.Digest,
|
||||
Labels: map[string]string{
|
||||
"containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339Nano),
|
||||
},
|
||||
}, "labels.containerd.io/gc.root")
|
||||
if err2 != nil {
|
||||
return c.writer(ctx, ref, size, expected, retries-1)
|
||||
return c.writer(ctx, retries-1, opts...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,8 +107,8 @@ type noGCWriter struct {
|
|||
content.Writer
|
||||
}
|
||||
|
||||
func (cs *noGCContentStore) Writer(ctx context.Context, ref string, size int64, expected digest.Digest) (content.Writer, error) {
|
||||
w, err := cs.Store.Writer(ctx, ref, size, expected)
|
||||
func (cs *noGCContentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
|
||||
w, err := cs.Store.Writer(ctx, opts...)
|
||||
return &noGCWriter{w}, err
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -33,27 +34,33 @@ type buffer struct {
|
|||
refs map[string]struct{}
|
||||
}
|
||||
|
||||
func (b *buffer) Writer(ctx context.Context, ref string, size int64, expected digest.Digest) (content.Writer, error) {
|
||||
func (b *buffer) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
|
||||
var wOpts content.WriterOpts
|
||||
for _, opt := range opts {
|
||||
if err := opt(&wOpts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
b.mu.Lock()
|
||||
if _, ok := b.refs[ref]; ok {
|
||||
return nil, errors.Wrapf(errdefs.ErrUnavailable, "ref %s locked", ref)
|
||||
if _, ok := b.refs[wOpts.Ref]; ok {
|
||||
return nil, errors.Wrapf(errdefs.ErrUnavailable, "ref %s locked", wOpts.Ref)
|
||||
}
|
||||
b.mu.Unlock()
|
||||
return &bufferedWriter{
|
||||
main: b,
|
||||
digester: digest.Canonical.Digester(),
|
||||
buffer: bytes.NewBuffer(nil),
|
||||
expected: expected,
|
||||
expected: wOpts.Desc.Digest,
|
||||
releaseRef: func() {
|
||||
b.mu.Lock()
|
||||
delete(b.refs, ref)
|
||||
delete(b.refs, wOpts.Ref)
|
||||
b.mu.Unlock()
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *buffer) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) {
|
||||
r, err := b.getBytesReader(ctx, dgst)
|
||||
func (b *buffer) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
|
||||
r, err := b.getBytesReader(ctx, desc.Digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -19,26 +20,26 @@ func TestReadWrite(t *testing.T) {
|
|||
|
||||
b := NewBuffer()
|
||||
|
||||
err := content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foo0")), -1, "")
|
||||
err := content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foo0")), ocispec.Descriptor{Size: -1})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foo1")), 4, "")
|
||||
err = content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foo1")), ocispec.Descriptor{Size: 4})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foo2")), 3, "")
|
||||
err = content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foo2")), ocispec.Descriptor{Size: 3})
|
||||
require.Error(t, err)
|
||||
|
||||
err = content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foo3")), -1, digest.FromBytes([]byte("foo4")))
|
||||
err = content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foo3")), ocispec.Descriptor{Size: -1, Digest: digest.FromBytes([]byte("foo4"))})
|
||||
require.Error(t, err)
|
||||
|
||||
err = content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foo4")), -1, digest.FromBytes([]byte("foo4")))
|
||||
err = content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foo4")), ocispec.Descriptor{Size: -1, Digest: digest.FromBytes([]byte("foo4"))})
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := content.ReadBlob(ctx, b, digest.FromBytes([]byte("foo1")))
|
||||
dt, err := content.ReadBlob(ctx, b, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foo1"))})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(dt), "foo1")
|
||||
|
||||
_, err = content.ReadBlob(ctx, b, digest.FromBytes([]byte("foo3")))
|
||||
_, err = content.ReadBlob(ctx, b, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foo3"))})
|
||||
require.Error(t, err)
|
||||
require.Equal(t, errors.Cause(err), errdefs.ErrNotFound)
|
||||
}
|
||||
|
@ -49,10 +50,10 @@ func TestReaderAt(t *testing.T) {
|
|||
|
||||
b := NewBuffer()
|
||||
|
||||
err := content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foobar")), -1, "")
|
||||
err := content.WriteBlob(ctx, b, "foo", bytes.NewBuffer([]byte("foobar")), ocispec.Descriptor{Size: -1})
|
||||
require.NoError(t, err)
|
||||
|
||||
rdr, err := b.ReaderAt(ctx, digest.FromBytes([]byte("foobar")))
|
||||
rdr, err := b.ReaderAt(ctx, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foobar"))})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, int64(6), rdr.Size())
|
||||
|
|
|
@ -21,7 +21,7 @@ type localFetcher struct {
|
|||
}
|
||||
|
||||
func (f *localFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) {
|
||||
r, err := f.Provider.ReaderAt(ctx, desc.Digest)
|
||||
r, err := f.Provider.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@ func TestCopy(t *testing.T) {
|
|||
b0 := NewBuffer()
|
||||
b1 := NewBuffer()
|
||||
|
||||
err := content.WriteBlob(ctx, b0, "foo", bytes.NewBuffer([]byte("foobar")), -1, "")
|
||||
err := content.WriteBlob(ctx, b0, "foo", bytes.NewBuffer([]byte("foobar")), ocispec.Descriptor{Size: -1})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = Copy(ctx, b1, b0, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foobar")), Size: -1})
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := content.ReadBlob(ctx, b1, digest.FromBytes([]byte("foobar")))
|
||||
dt, err := content.ReadBlob(ctx, b1, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foobar"))})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(dt), "foobar")
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/remotes"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -24,9 +23,9 @@ type fetchedProvider struct {
|
|||
desc ocispec.Descriptor
|
||||
}
|
||||
|
||||
func (p *fetchedProvider) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) {
|
||||
if dgst != p.desc.Digest {
|
||||
return nil, errors.Wrapf(errdefs.ErrNotFound, "content %v", dgst)
|
||||
func (p *fetchedProvider) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
|
||||
if desc.Digest != p.desc.Digest {
|
||||
return nil, errors.Wrapf(errdefs.ErrNotFound, "content %v", desc.Digest)
|
||||
}
|
||||
|
||||
rc, err := p.f.Fetch(ctx, p.desc)
|
||||
|
|
|
@ -19,7 +19,7 @@ func TestFetcher(t *testing.T) {
|
|||
|
||||
b0 := NewBuffer()
|
||||
|
||||
err := content.WriteBlob(ctx, b0, "foo", bytes.NewBuffer([]byte("foobar")), -1, "")
|
||||
err := content.WriteBlob(ctx, b0, "foo", bytes.NewBuffer([]byte("foobar")), ocispec.Descriptor{Size: -1})
|
||||
require.NoError(t, err)
|
||||
|
||||
f := &localFetcher{b0}
|
||||
|
@ -29,11 +29,11 @@ func TestFetcher(t *testing.T) {
|
|||
err = Copy(ctx, b1, p, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foobar")), Size: -1})
|
||||
require.NoError(t, err)
|
||||
|
||||
dt, err := content.ReadBlob(ctx, b1, digest.FromBytes([]byte("foobar")))
|
||||
dt, err := content.ReadBlob(ctx, b1, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foobar"))})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(dt), "foobar")
|
||||
|
||||
rdr, err := p.ReaderAt(ctx, digest.FromBytes([]byte("foobar")))
|
||||
rdr, err := p.ReaderAt(ctx, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foobar"))})
|
||||
require.NoError(t, err)
|
||||
|
||||
buf := make([]byte, 3)
|
||||
|
@ -55,7 +55,7 @@ func TestSlowFetch(t *testing.T) {
|
|||
f := &dummySlowFetcher{}
|
||||
p := FromFetcher(f, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foobar")), Size: -1})
|
||||
|
||||
rdr, err := p.ReaderAt(ctx, digest.FromBytes([]byte("foobar")))
|
||||
rdr, err := p.ReaderAt(ctx, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foobar"))})
|
||||
require.NoError(t, err)
|
||||
|
||||
buf := make([]byte, 3)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -23,17 +24,17 @@ type MultiProvider struct {
|
|||
sub map[digest.Digest]content.Provider
|
||||
}
|
||||
|
||||
func (mp *MultiProvider) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) {
|
||||
func (mp *MultiProvider) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
|
||||
mp.mu.RLock()
|
||||
if p, ok := mp.sub[dgst]; ok {
|
||||
if p, ok := mp.sub[desc.Digest]; ok {
|
||||
mp.mu.RUnlock()
|
||||
return p.ReaderAt(ctx, dgst)
|
||||
return p.ReaderAt(ctx, desc)
|
||||
}
|
||||
mp.mu.RUnlock()
|
||||
if mp.base == nil {
|
||||
return nil, errors.Wrapf(errdefs.ErrNotFound, "content %v", dgst)
|
||||
return nil, errors.Wrapf(errdefs.ErrNotFound, "content %v", desc.Digest)
|
||||
}
|
||||
return mp.base.ReaderAt(ctx, dgst)
|
||||
return mp.base.ReaderAt(ctx, desc)
|
||||
}
|
||||
|
||||
func (mp *MultiProvider) Add(dgst digest.Digest, p content.Provider) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -19,25 +20,25 @@ func TestMultiProvider(t *testing.T) {
|
|||
b0 := NewBuffer()
|
||||
b1 := NewBuffer()
|
||||
|
||||
err := content.WriteBlob(ctx, b0, "foo", bytes.NewBuffer([]byte("foo0")), -1, "")
|
||||
err := content.WriteBlob(ctx, b0, "foo", bytes.NewBuffer([]byte("foo0")), ocispec.Descriptor{Size: -1})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = content.WriteBlob(ctx, b1, "foo", bytes.NewBuffer([]byte("foo1")), -1, "")
|
||||
err = content.WriteBlob(ctx, b1, "foo", bytes.NewBuffer([]byte("foo1")), ocispec.Descriptor{Size: -1})
|
||||
require.NoError(t, err)
|
||||
|
||||
mp := NewMultiProvider(nil)
|
||||
mp.Add(digest.FromBytes([]byte("foo0")), b0)
|
||||
mp.Add(digest.FromBytes([]byte("foo1")), b1)
|
||||
|
||||
dt, err := content.ReadBlob(ctx, mp, digest.FromBytes([]byte("foo0")))
|
||||
dt, err := content.ReadBlob(ctx, mp, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foo0"))})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(dt), "foo0")
|
||||
|
||||
dt, err = content.ReadBlob(ctx, mp, digest.FromBytes([]byte("foo1")))
|
||||
dt, err = content.ReadBlob(ctx, mp, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foo1"))})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(dt), "foo1")
|
||||
|
||||
_, err = content.ReadBlob(ctx, mp, digest.FromBytes([]byte("foo2")))
|
||||
_, err = content.ReadBlob(ctx, mp, ocispec.Descriptor{Digest: digest.FromBytes([]byte("foo2"))})
|
||||
require.Error(t, err)
|
||||
require.Equal(t, errors.Cause(err), errdefs.ErrNotFound)
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ type tarRecord struct {
|
|||
func dockerManifestRecord(ctx context.Context, provider content.Provider, desc ocispec.Descriptor, name string) (*tarRecord, error) {
|
||||
switch desc.MediaType {
|
||||
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
|
||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ func blobRecord(cs content.Provider, desc ocispec.Descriptor) tarRecord {
|
|||
Typeflag: tar.TypeReg,
|
||||
},
|
||||
CopyTo: func(ctx context.Context, w io.Writer) (int64, error) {
|
||||
r, err := cs.ReaderAt(ctx, desc.Digest)
|
||||
r, err := cs.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
@ -28,28 +28,25 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester
|
|||
return "", nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
dgst := ref.Digest()
|
||||
var desc *ocispec.Descriptor
|
||||
if dgst != "" {
|
||||
ra, err := ingester.ReaderAt(ctx, dgst)
|
||||
desc := ocispec.Descriptor{
|
||||
Digest: ref.Digest(),
|
||||
}
|
||||
if desc.Digest != "" {
|
||||
ra, err := ingester.ReaderAt(ctx, desc)
|
||||
if err == nil {
|
||||
desc.Size = ra.Size()
|
||||
mt, err := DetectManifestMediaType(ra)
|
||||
if err == nil {
|
||||
desc = &ocispec.Descriptor{
|
||||
Size: ra.Size(),
|
||||
Digest: dgst,
|
||||
MediaType: mt,
|
||||
}
|
||||
desc.MediaType = mt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if desc == nil {
|
||||
_, desc2, err := resolver.Resolve(ctx, ref.String())
|
||||
// use resolver if desc is incomplete
|
||||
if desc.MediaType == "" {
|
||||
_, desc, err = resolver.Resolve(ctx, ref.String())
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
desc = &desc2
|
||||
}
|
||||
|
||||
fetcher, err := resolver.Fetcher(ctx, ref.String())
|
||||
|
@ -61,15 +58,15 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester
|
|||
remotes.FetchHandler(ingester, fetcher),
|
||||
childrenConfigHandler(ingester, platform),
|
||||
}
|
||||
if err := images.Dispatch(ctx, images.Handlers(handlers...), *desc); err != nil {
|
||||
if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
config, err := images.Config(ctx, ingester, *desc, platform)
|
||||
config, err := images.Config(ctx, ingester, desc, platform)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
dt, err := content.ReadBlob(ctx, ingester, config.Digest)
|
||||
dt, err := content.ReadBlob(ctx, ingester, config)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
@ -82,7 +79,7 @@ func childrenConfigHandler(provider content.Provider, platform string) images.Ha
|
|||
var descs []ocispec.Descriptor
|
||||
switch desc.MediaType {
|
||||
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
|
||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -96,7 +93,7 @@ func childrenConfigHandler(provider content.Provider, platform string) images.Ha
|
|||
|
||||
descs = append(descs, manifest.Config)
|
||||
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
|
||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -47,20 +47,20 @@ func (p *Puller) Resolve(ctx context.Context) (string, ocispec.Descriptor, error
|
|||
p.resolveOnce.Do(func() {
|
||||
resolveProgressDone := oneOffProgress(ctx, "resolve "+p.Src.String())
|
||||
|
||||
dgst := p.Src.Digest()
|
||||
if dgst != "" {
|
||||
info, err := p.ContentStore.Info(ctx, dgst)
|
||||
desc := ocispec.Descriptor{
|
||||
Digest: p.Src.Digest(),
|
||||
}
|
||||
if desc.Digest != "" {
|
||||
info, err := p.ContentStore.Info(ctx, desc.Digest)
|
||||
if err == nil {
|
||||
desc.Size = info.Size
|
||||
p.ref = p.Src.String()
|
||||
ra, err := p.ContentStore.ReaderAt(ctx, dgst)
|
||||
ra, err := p.ContentStore.ReaderAt(ctx, desc)
|
||||
if err == nil {
|
||||
mt, err := imageutil.DetectManifestMediaType(ra)
|
||||
if err == nil {
|
||||
p.desc = ocispec.Descriptor{
|
||||
Size: info.Size,
|
||||
Digest: dgst,
|
||||
MediaType: mt,
|
||||
}
|
||||
desc.MediaType = mt
|
||||
p.desc = desc
|
||||
resolveProgressDone(nil)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -41,7 +41,9 @@ func getCredentialsFunc(ctx context.Context, sm *session.Manager) func(string) (
|
|||
}
|
||||
|
||||
func Push(ctx context.Context, sm *session.Manager, cs content.Provider, dgst digest.Digest, ref string, insecure bool) error {
|
||||
|
||||
desc := ocispec.Descriptor{
|
||||
Digest: dgst,
|
||||
}
|
||||
parsed, err := reference.ParseNormalizedNamed(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -83,7 +85,7 @@ func Push(ctx context.Context, sm *session.Manager, cs content.Provider, dgst di
|
|||
pushHandler,
|
||||
)
|
||||
|
||||
ra, err := cs.ReaderAt(ctx, dgst)
|
||||
ra, err := cs.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -138,7 +140,7 @@ func childrenHandler(provider content.Provider) images.HandlerFunc {
|
|||
var descs []ocispec.Descriptor
|
||||
switch desc.MediaType {
|
||||
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
|
||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -153,7 +155,7 @@ func childrenHandler(provider content.Provider) images.HandlerFunc {
|
|||
descs = append(descs, manifest.Config)
|
||||
descs = append(descs, manifest.Layers...)
|
||||
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
|
||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ github.com/davecgh/go-spew v1.1.0
|
|||
github.com/pmezard/go-difflib v1.0.0
|
||||
golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993
|
||||
|
||||
github.com/containerd/containerd e1428ef05460da40720d622c803262e6fc8d3477
|
||||
github.com/containerd/containerd 63522d9eaa5a0443d225642c4b6f4f5fdedf932b
|
||||
github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788
|
||||
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
|
||||
github.com/sirupsen/logrus v1.0.0
|
||||
|
@ -23,7 +23,7 @@ github.com/Microsoft/go-winio v0.4.7
|
|||
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
|
||||
github.com/opencontainers/runtime-spec v1.0.1
|
||||
github.com/containerd/go-runc f271fa2021de855d4d918dbef83c5fe19db1bdd5
|
||||
github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925
|
||||
github.com/containerd/console 9290d21dc56074581f619579c43d970b4514bc08
|
||||
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
|
||||
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
|
||||
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
int copies;
|
||||
int non_copies;
|
||||
|
||||
void *realloc(void *ptr, size_t size) {
|
||||
static void *(*real_realloc)(void*, size_t) = NULL;
|
||||
if (real_realloc == NULL) {
|
||||
real_realloc = (void* (*) (void*, size_t)) dlsym(RTLD_NEXT, "realloc");
|
||||
}
|
||||
|
||||
void *ret_ptr = (*real_realloc)(ptr, size);
|
||||
|
||||
if (ret_ptr == ptr) {
|
||||
non_copies++;
|
||||
} else {
|
||||
copies++;
|
||||
}
|
||||
|
||||
return ret_ptr;
|
||||
}
|
||||
|
||||
|
||||
struct TMemoryBuffer {
|
||||
void* ptr;
|
||||
int size;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int num_buffers;
|
||||
int init_size;
|
||||
int max_size;
|
||||
int doublings;
|
||||
int iterations;
|
||||
|
||||
if (argc < 6 ||
|
||||
argc > 7 ||
|
||||
(num_buffers = atoi(argv[1])) == 0 ||
|
||||
(init_size = atoi(argv[2])) == 0 ||
|
||||
(max_size = atoi(argv[3])) == 0 ||
|
||||
init_size > max_size ||
|
||||
(iterations = atoi(argv[4])) == 0 ||
|
||||
(doublings = atoi(argv[5])) == 0 ||
|
||||
(argc == 7 && atoi(argv[6]) == 0)) {
|
||||
fprintf(stderr, "usage: realloc_test <num_buffers> <init_size> <max_size> <doublings> <iterations> [seed]\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for ( int i = 0 ; i < argc ; i++ ) {
|
||||
printf("%s ", argv[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
if (argc == 7) {
|
||||
srand(atoi(argv[6]));
|
||||
} else {
|
||||
srand(time(NULL));
|
||||
}
|
||||
|
||||
struct TMemoryBuffer* buffers = calloc(num_buffers, sizeof(*buffers));
|
||||
if (buffers == NULL) abort();
|
||||
|
||||
for ( int i = 0 ; i < num_buffers ; i++ ) {
|
||||
buffers[i].size = max_size;
|
||||
}
|
||||
|
||||
while (iterations --> 0) {
|
||||
for ( int i = 0 ; i < doublings * num_buffers ; i++ ) {
|
||||
struct TMemoryBuffer* buf = &buffers[rand() % num_buffers];
|
||||
buf->size *= 2;
|
||||
if (buf->size <= max_size) {
|
||||
buf->ptr = realloc(buf->ptr, buf->size);
|
||||
} else {
|
||||
free(buf->ptr);
|
||||
buf->size = init_size;
|
||||
buf->ptr = malloc(buf->size);
|
||||
}
|
||||
if (buf->ptr == NULL) abort();
|
||||
}
|
||||
}
|
||||
|
||||
printf("Non-copied %d/%d (%.2f%%)\n", non_copies, copies + non_copies, 100.0 * non_copies / (copies + non_copies));
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
Thrift Tutorial
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Tutorial
|
||||
========
|
||||
|
||||
1) First things first, you'll need to install the Thrift compiler and the
|
||||
language libraries. Do that using the instructions in the top level
|
||||
README.md file.
|
||||
|
||||
2) Read tutorial.thrift to learn about the syntax of a Thrift file
|
||||
|
||||
3) Compile the code for the language of your choice:
|
||||
|
||||
$ thrift
|
||||
$ thrift -r --gen cpp tutorial.thrift
|
||||
|
||||
4) Take a look at the generated code.
|
||||
|
||||
5) Look in the language directories for sample client/server code.
|
||||
|
||||
6) That's about it for now. This tutorial is intentionally brief. It should be
|
||||
just enough to get you started and ready to build your own project.
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
|
||||
#include <thrift/c_glib/transport/thrift_buffered_transport.h>
|
||||
#include <thrift/c_glib/transport/thrift_socket.h>
|
||||
|
||||
#include "gen-c_glib/calculator.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
ThriftSocket *socket;
|
||||
ThriftTransport *transport;
|
||||
ThriftProtocol *protocol;
|
||||
CalculatorIf *client;
|
||||
|
||||
GError *error = NULL;
|
||||
InvalidOperation *invalid_operation = NULL;
|
||||
|
||||
Work *work;
|
||||
|
||||
gint32 sum;
|
||||
gint32 diff;
|
||||
|
||||
int exit_status = 0;
|
||||
|
||||
#if (!GLIB_CHECK_VERSION (2, 36, 0))
|
||||
g_type_init ();
|
||||
#endif
|
||||
|
||||
socket = g_object_new (THRIFT_TYPE_SOCKET,
|
||||
"hostname", "localhost",
|
||||
"port", 9090,
|
||||
NULL);
|
||||
transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
|
||||
"transport", socket,
|
||||
NULL);
|
||||
protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL,
|
||||
"transport", transport,
|
||||
NULL);
|
||||
|
||||
thrift_transport_open (transport, &error);
|
||||
|
||||
|
||||
/* In the C (GLib) implementation of Thrift, service methods on the
|
||||
server are accessed via a generated client class that implements
|
||||
the service interface. In this tutorial, we access a Calculator
|
||||
service through an instance of CalculatorClient, which implements
|
||||
CalculatorIf. */
|
||||
client = g_object_new (TYPE_CALCULATOR_CLIENT,
|
||||
"input_protocol", protocol,
|
||||
"output_protocol", protocol,
|
||||
NULL);
|
||||
|
||||
/* Each of the client methods requires at least two parameters: A
|
||||
pointer to the client-interface implementation (the client
|
||||
object), and a handle to a GError structure to receive
|
||||
information about any error that occurs.
|
||||
|
||||
On success, client methods return TRUE. A return value of FALSE
|
||||
indicates an error occurred and the error parameter has been
|
||||
set. */
|
||||
if (!error && calculator_if_ping (client, &error)) {
|
||||
puts ("ping()");
|
||||
}
|
||||
|
||||
/* Service methods that return a value do so by passing the result
|
||||
back via an output parameter (here, "sum"). */
|
||||
if (!error && calculator_if_add (client, &sum, 1, 1, &error)) {
|
||||
printf ("1+1=%d\n", sum);
|
||||
}
|
||||
|
||||
/* Thrift structs are implemented as GObjects, with each of the
|
||||
struct's members exposed as an object property. */
|
||||
work = g_object_new (TYPE_WORK, NULL);
|
||||
|
||||
if (!error) {
|
||||
g_object_set (work,
|
||||
"num1", 1,
|
||||
"num2", 0,
|
||||
"op", OPERATION_DIVIDE,
|
||||
NULL);
|
||||
|
||||
/* Exceptions are passed back from service methods in a manner
|
||||
similar to return values. */
|
||||
if (calculator_if_calculate (client,
|
||||
NULL,
|
||||
1,
|
||||
work,
|
||||
&invalid_operation,
|
||||
&error)) {
|
||||
puts ("Whoa? We can divide by zero!");
|
||||
}
|
||||
else {
|
||||
if (invalid_operation) {
|
||||
gchar *why;
|
||||
|
||||
/* Like structs, exceptions are implemented as objects with
|
||||
properties. */
|
||||
g_object_get (invalid_operation, "why", &why, NULL);
|
||||
|
||||
printf ("InvalidOperation: %s\n", why);
|
||||
|
||||
if (why != NULL)
|
||||
g_free (why);
|
||||
g_object_unref (invalid_operation);
|
||||
invalid_operation = NULL;
|
||||
}
|
||||
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
/* Struct objects can be reused across method invocations. */
|
||||
g_object_set (work,
|
||||
"num1", 15,
|
||||
"num2", 10,
|
||||
"op", OPERATION_SUBTRACT,
|
||||
NULL);
|
||||
|
||||
if (calculator_if_calculate (client,
|
||||
&diff,
|
||||
1,
|
||||
work,
|
||||
&invalid_operation,
|
||||
&error)) {
|
||||
printf ("15-10=%d\n", diff);
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (work);
|
||||
|
||||
if (!error) {
|
||||
SharedStruct *shared_struct;
|
||||
gchar *value;
|
||||
|
||||
shared_struct = g_object_new (TYPE_SHARED_STRUCT, NULL);
|
||||
|
||||
/* As defined in the Thrift file, the Calculator service extends
|
||||
the SharedService service. Correspondingly, in the generated
|
||||
code CalculatorIf inherits from SharedServiceIf, and the parent
|
||||
service's methods are accessible through a simple cast. */
|
||||
if (shared_service_client_get_struct (SHARED_SERVICE_IF (client),
|
||||
&shared_struct,
|
||||
1,
|
||||
&error)) {
|
||||
g_object_get (shared_struct, "value", &value, NULL);
|
||||
printf ("Check log: %s\n", value);
|
||||
g_free (value);
|
||||
}
|
||||
|
||||
g_object_unref (shared_struct);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
printf ("ERROR: %s\n", error->message);
|
||||
g_clear_error (&error);
|
||||
|
||||
exit_status = 1;
|
||||
}
|
||||
|
||||
thrift_transport_close (transport, NULL);
|
||||
|
||||
g_object_unref (client);
|
||||
g_object_unref (protocol);
|
||||
g_object_unref (transport);
|
||||
g_object_unref (socket);
|
||||
|
||||
return exit_status;
|
||||
}
|
|
@ -0,0 +1,527 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <thrift/c_glib/thrift.h>
|
||||
#include <thrift/c_glib/protocol/thrift_binary_protocol_factory.h>
|
||||
#include <thrift/c_glib/protocol/thrift_protocol_factory.h>
|
||||
#include <thrift/c_glib/server/thrift_server.h>
|
||||
#include <thrift/c_glib/server/thrift_simple_server.h>
|
||||
#include <thrift/c_glib/transport/thrift_buffered_transport_factory.h>
|
||||
#include <thrift/c_glib/transport/thrift_server_socket.h>
|
||||
#include <thrift/c_glib/transport/thrift_server_transport.h>
|
||||
|
||||
#include "gen-c_glib/calculator.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* In the C (GLib) implementation of Thrift, the actual work done by a
|
||||
server---that is, the code that runs when a client invokes a
|
||||
service method---is defined in a separate "handler" class that
|
||||
implements the service interface. Here we define the
|
||||
TutorialCalculatorHandler class, which implements the CalculatorIf
|
||||
interface and provides the behavior expected by tutorial clients.
|
||||
(Typically this code would be placed in its own module but for
|
||||
clarity this tutorial is presented entirely in a single file.)
|
||||
|
||||
For each service the Thrift compiler generates an abstract base
|
||||
class from which handler implementations should inherit. In our
|
||||
case TutorialCalculatorHandler inherits from CalculatorHandler,
|
||||
defined in gen-c_glib/calculator.h.
|
||||
|
||||
If you're new to GObject, try not to be intimidated by the quantity
|
||||
of code here---much of it is boilerplate and can mostly be
|
||||
copied-and-pasted from existing work. For more information refer to
|
||||
the GObject Reference Manual, available online at
|
||||
https://developer.gnome.org/gobject/. */
|
||||
|
||||
#define TYPE_TUTORIAL_CALCULATOR_HANDLER \
|
||||
(tutorial_calculator_handler_get_type ())
|
||||
|
||||
#define TUTORIAL_CALCULATOR_HANDLER(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||
TYPE_TUTORIAL_CALCULATOR_HANDLER, \
|
||||
TutorialCalculatorHandler))
|
||||
#define TUTORIAL_CALCULATOR_HANDLER_CLASS(c) \
|
||||
(G_TYPE_CHECK_CLASS_CAST ((c), \
|
||||
TYPE_TUTORIAL_CALCULATOR_HANDLER, \
|
||||
TutorialCalculatorHandlerClass))
|
||||
#define IS_TUTORIAL_CALCULATOR_HANDLER(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
||||
TYPE_TUTORIAL_CALCULATOR_HANDLER))
|
||||
#define IS_TUTORIAL_CALCULATOR_HANDLER_CLASS(c) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE ((c), \
|
||||
TYPE_TUTORIAL_CALCULATOR_HANDLER))
|
||||
#define TUTORIAL_CALCULATOR_HANDLER_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
|
||||
TYPE_TUTORIAL_CALCULATOR_HANDLER, \
|
||||
TutorialCalculatorHandlerClass))
|
||||
|
||||
struct _TutorialCalculatorHandler {
|
||||
CalculatorHandler parent_instance;
|
||||
|
||||
/* private */
|
||||
GHashTable *log;
|
||||
};
|
||||
typedef struct _TutorialCalculatorHandler TutorialCalculatorHandler;
|
||||
|
||||
struct _TutorialCalculatorHandlerClass {
|
||||
CalculatorHandlerClass parent_class;
|
||||
};
|
||||
typedef struct _TutorialCalculatorHandlerClass TutorialCalculatorHandlerClass;
|
||||
|
||||
GType tutorial_calculator_handler_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
/* The implementation of TutorialCalculatorHandler follows. */
|
||||
|
||||
G_DEFINE_TYPE (TutorialCalculatorHandler,
|
||||
tutorial_calculator_handler,
|
||||
TYPE_CALCULATOR_HANDLER)
|
||||
|
||||
/* Each of a handler's methods accepts at least two parameters: A
|
||||
pointer to the service-interface implementation (the handler object
|
||||
itself) and a handle to a GError structure to receive information
|
||||
about any error that occurs.
|
||||
|
||||
On success, a handler method returns TRUE. A return value of FALSE
|
||||
indicates an error occurred and the error parameter has been
|
||||
set. (Methods should not return FALSE without first setting the
|
||||
error parameter.) */
|
||||
static gboolean
|
||||
tutorial_calculator_handler_ping (CalculatorIf *iface,
|
||||
GError **error)
|
||||
{
|
||||
THRIFT_UNUSED_VAR (iface);
|
||||
THRIFT_UNUSED_VAR (error);
|
||||
|
||||
puts ("ping()");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Service-method parameters are passed through as parameters to the
|
||||
handler method.
|
||||
|
||||
If the service method returns a value an output parameter, _return,
|
||||
is additionally passed to the handler method. This parameter should
|
||||
be set appropriately before the method returns, whenever it
|
||||
succeeds.
|
||||
|
||||
The return value from this method happens to be of a base type,
|
||||
i32, but note if a method returns a complex type such as a map or
|
||||
list *_return will point to a pre-allocated data structure that
|
||||
does not need to be re-allocated and should not be destroyed. */
|
||||
static gboolean
|
||||
tutorial_calculator_handler_add (CalculatorIf *iface,
|
||||
gint32 *_return,
|
||||
const gint32 num1,
|
||||
const gint32 num2,
|
||||
GError **error)
|
||||
{
|
||||
THRIFT_UNUSED_VAR (iface);
|
||||
THRIFT_UNUSED_VAR (error);
|
||||
|
||||
printf ("add(%d,%d)\n", num1, num2);
|
||||
*_return = num1 + num2;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Any handler method can return a ThriftApplicationException to the
|
||||
client by setting its error parameter appropriately and returning
|
||||
FALSE. See the ThriftApplicationExceptionError enumeration defined
|
||||
in thrift_application_exception.h for a list of recognized
|
||||
exception types (GError codes).
|
||||
|
||||
If a service method can also throw a custom exception (that is, one
|
||||
defined in the .thrift file) an additional output parameter will be
|
||||
provided (here, "ouch") to hold an instance of the exception, when
|
||||
necessary. Note there will be a separate parameter added for each
|
||||
type of exception the method can throw.
|
||||
|
||||
Unlike return values, exception objects are never pre-created; this
|
||||
is always the responsibility of the handler method. */
|
||||
static gboolean
|
||||
tutorial_calculator_handler_calculate (CalculatorIf *iface,
|
||||
gint32 *_return,
|
||||
const gint32 logid,
|
||||
const Work *w,
|
||||
InvalidOperation **ouch,
|
||||
GError **error)
|
||||
{
|
||||
TutorialCalculatorHandler *self;
|
||||
|
||||
gint *log_key;
|
||||
gchar log_value[12];
|
||||
SharedStruct *log_struct;
|
||||
|
||||
gint num1;
|
||||
gint num2;
|
||||
Operation op;
|
||||
gboolean result = TRUE;
|
||||
|
||||
THRIFT_UNUSED_VAR (error);
|
||||
|
||||
g_return_val_if_fail (IS_TUTORIAL_CALCULATOR_HANDLER (iface),
|
||||
FALSE);
|
||||
self = TUTORIAL_CALCULATOR_HANDLER (iface);
|
||||
|
||||
/* Remember: Exception objects are never pre-created */
|
||||
g_assert (*ouch == NULL);
|
||||
|
||||
/* Fetch the contents of our Work parameter.
|
||||
|
||||
Note that integer properties of thirty-two bits or fewer in width
|
||||
are _always_ of type gint, regardless of the range of values they
|
||||
hold. A common error is trying to retrieve, say, a structure
|
||||
member defined in the .thrift file as type i16 into a variable of
|
||||
type gint16, which will clobber variables adjacent on the
|
||||
stack. Remember: If you're retrieving an integer property the
|
||||
receiving variable must be of either type gint or gint64, as
|
||||
appropriate. */
|
||||
g_object_get ((Work *)w,
|
||||
"num1", &num1,
|
||||
"num2", &num2,
|
||||
"op", &op,
|
||||
NULL);
|
||||
|
||||
printf ("calculate(%d,{%d,%d,%d})\n", logid, op, num1, num2);
|
||||
|
||||
switch (op) {
|
||||
case OPERATION_ADD:
|
||||
*_return = num1 + num2;
|
||||
break;
|
||||
|
||||
case OPERATION_SUBTRACT:
|
||||
*_return = num1 - num2;
|
||||
break;
|
||||
|
||||
case OPERATION_MULTIPLY:
|
||||
*_return = num1 * num2;
|
||||
break;
|
||||
|
||||
case OPERATION_DIVIDE:
|
||||
if (num2 == 0) {
|
||||
/* For each custom exception type a subclass of ThriftStruct is
|
||||
generated by the Thrift compiler. Throw an exception by
|
||||
setting the corresponding output parameter to a new instance
|
||||
of its type and returning FALSE. */
|
||||
*ouch = g_object_new (TYPE_INVALID_OPERATION,
|
||||
"whatOp", op,
|
||||
"why", g_strdup ("Cannot divide by 0"),
|
||||
NULL);
|
||||
result = FALSE;
|
||||
|
||||
/* Note the call to g_strdup above: All the memory used by a
|
||||
ThriftStruct's properties belongs to the object itself and
|
||||
will be freed on destruction. Removing this call to g_strdup
|
||||
will lead to a segmentation fault as the object tries to
|
||||
release memory allocated statically to the program. */
|
||||
}
|
||||
else {
|
||||
*_return = num1 / num2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
*ouch = g_object_new (TYPE_INVALID_OPERATION,
|
||||
"whatOp", op,
|
||||
"why", g_strdup ("Invalid Operation"),
|
||||
NULL);
|
||||
result = FALSE;
|
||||
}
|
||||
|
||||
/* On success, log a record of the result to our hash table */
|
||||
if (result) {
|
||||
log_key = g_malloc (sizeof *log_key);
|
||||
*log_key = logid;
|
||||
|
||||
snprintf (log_value, sizeof log_value, "%d", *_return);
|
||||
|
||||
log_struct = g_object_new (TYPE_SHARED_STRUCT,
|
||||
"key", *log_key,
|
||||
"value", g_strdup (log_value),
|
||||
NULL);
|
||||
g_hash_table_replace (self->log, log_key, log_struct);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* A one-way method has the same signature as an equivalent, regular
|
||||
method that returns no value. */
|
||||
static gboolean
|
||||
tutorial_calculator_handler_zip (CalculatorIf *iface,
|
||||
GError **error)
|
||||
{
|
||||
THRIFT_UNUSED_VAR (iface);
|
||||
THRIFT_UNUSED_VAR (error);
|
||||
|
||||
puts ("zip()");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* As specified in the .thrift file (tutorial.thrift), the Calculator
|
||||
service extends the SharedService service. Correspondingly, in the
|
||||
generated code the Calculator interface, CalculatorIf, extends the
|
||||
SharedService interface, SharedServiceIf, and subclasses of
|
||||
CalculatorHandler should implement its methods as well.
|
||||
|
||||
Here we provide an implementation for the getStruct method from the
|
||||
parent service. */
|
||||
static gboolean
|
||||
tutorial_calculator_handler_get_struct (SharedServiceIf *iface,
|
||||
SharedStruct **_return,
|
||||
const gint32 key32,
|
||||
GError **error)
|
||||
{
|
||||
gint key = (gint)key32;
|
||||
TutorialCalculatorHandler *self;
|
||||
SharedStruct *log_struct;
|
||||
gint log_key;
|
||||
gchar *log_value;
|
||||
|
||||
THRIFT_UNUSED_VAR (error);
|
||||
|
||||
g_return_val_if_fail (IS_TUTORIAL_CALCULATOR_HANDLER (iface),
|
||||
FALSE);
|
||||
self = TUTORIAL_CALCULATOR_HANDLER (iface);
|
||||
|
||||
/* Remember: Complex return types are always pre-created and need
|
||||
only be populated */
|
||||
g_assert (*_return != NULL);
|
||||
|
||||
printf ("getStruct(%d)\n", key);
|
||||
|
||||
/* If the key exists in our log, return the corresponding logged
|
||||
data (or an empty SharedStruct structure if it does not).
|
||||
|
||||
Incidentally, note we _must_ here copy the values from the hash
|
||||
table into the return structure. All memory used by the return
|
||||
structure belongs to the structure itself and will be freed once
|
||||
a response is sent to the client. If we merely freed *_return and
|
||||
set it to point to our hash-table entry, that would mean memory
|
||||
would be released (effectively, data erased) out of the hash
|
||||
table! */
|
||||
log_struct = g_hash_table_lookup (self->log, &key);
|
||||
if (log_struct != NULL) {
|
||||
g_object_get (log_struct,
|
||||
"key", &log_key,
|
||||
"value", &log_value,
|
||||
NULL);
|
||||
g_object_set (*_return,
|
||||
"key", log_key,
|
||||
"value", g_strdup (log_value),
|
||||
NULL);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* TutorialCalculatorHandler's instance finalizer (destructor) */
|
||||
static void
|
||||
tutorial_calculator_handler_finalize (GObject *object)
|
||||
{
|
||||
TutorialCalculatorHandler *self =
|
||||
TUTORIAL_CALCULATOR_HANDLER (object);
|
||||
|
||||
/* Free our calculation-log hash table */
|
||||
g_hash_table_unref (self->log);
|
||||
self->log = NULL;
|
||||
|
||||
/* Chain up to the parent class */
|
||||
G_OBJECT_CLASS (tutorial_calculator_handler_parent_class)->
|
||||
finalize (object);
|
||||
}
|
||||
|
||||
/* TutorialCalculatorHandler's instance initializer (constructor) */
|
||||
static void
|
||||
tutorial_calculator_handler_init (TutorialCalculatorHandler *self)
|
||||
{
|
||||
/* Create our calculation-log hash table */
|
||||
self->log = g_hash_table_new_full (g_int_hash,
|
||||
g_int_equal,
|
||||
g_free,
|
||||
g_object_unref);
|
||||
}
|
||||
|
||||
/* TutorialCalculatorHandler's class initializer */
|
||||
static void
|
||||
tutorial_calculator_handler_class_init (TutorialCalculatorHandlerClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
SharedServiceHandlerClass *shared_service_handler_class =
|
||||
SHARED_SERVICE_HANDLER_CLASS (klass);
|
||||
CalculatorHandlerClass *calculator_handler_class =
|
||||
CALCULATOR_HANDLER_CLASS (klass);
|
||||
|
||||
/* Register our destructor */
|
||||
gobject_class->finalize = tutorial_calculator_handler_finalize;
|
||||
|
||||
/* Register our implementations of CalculatorHandler's methods */
|
||||
calculator_handler_class->ping =
|
||||
tutorial_calculator_handler_ping;
|
||||
calculator_handler_class->add =
|
||||
tutorial_calculator_handler_add;
|
||||
calculator_handler_class->calculate =
|
||||
tutorial_calculator_handler_calculate;
|
||||
calculator_handler_class->zip =
|
||||
tutorial_calculator_handler_zip;
|
||||
|
||||
/* Register our implementation of SharedServiceHandler's method */
|
||||
shared_service_handler_class->get_struct =
|
||||
tutorial_calculator_handler_get_struct;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
/* That ends the implementation of TutorialCalculatorHandler.
|
||||
Everything below is fairly generic code that sets up a minimal
|
||||
Thrift server for tutorial clients. */
|
||||
|
||||
|
||||
/* Our server object, declared globally so it is accessible within the
|
||||
SIGINT signal handler */
|
||||
ThriftServer *server = NULL;
|
||||
|
||||
/* A flag that indicates whether the server was interrupted with
|
||||
SIGINT (i.e. Ctrl-C) so we can tell whether its termination was
|
||||
abnormal */
|
||||
gboolean sigint_received = FALSE;
|
||||
|
||||
/* Handle SIGINT ("Ctrl-C") signals by gracefully stopping the
|
||||
server */
|
||||
static void
|
||||
sigint_handler (int signal_number)
|
||||
{
|
||||
THRIFT_UNUSED_VAR (signal_number);
|
||||
|
||||
/* Take note we were called */
|
||||
sigint_received = TRUE;
|
||||
|
||||
/* Shut down the server gracefully */
|
||||
if (server != NULL)
|
||||
thrift_server_stop (server);
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
TutorialCalculatorHandler *handler;
|
||||
CalculatorProcessor *processor;
|
||||
|
||||
ThriftServerTransport *server_transport;
|
||||
ThriftTransportFactory *transport_factory;
|
||||
ThriftProtocolFactory *protocol_factory;
|
||||
|
||||
struct sigaction sigint_action;
|
||||
|
||||
GError *error = NULL;
|
||||
int exit_status = 0;
|
||||
|
||||
#if (!GLIB_CHECK_VERSION (2, 36, 0))
|
||||
g_type_init ();
|
||||
#endif
|
||||
|
||||
/* Create an instance of our handler, which provides the service's
|
||||
methods' implementation */
|
||||
handler =
|
||||
g_object_new (TYPE_TUTORIAL_CALCULATOR_HANDLER,
|
||||
NULL);
|
||||
|
||||
/* Create an instance of the service's processor, automatically
|
||||
generated by the Thrift compiler, which parses incoming messages
|
||||
and dispatches them to the appropriate method in the handler */
|
||||
processor =
|
||||
g_object_new (TYPE_CALCULATOR_PROCESSOR,
|
||||
"handler", handler,
|
||||
NULL);
|
||||
|
||||
/* Create our server socket, which binds to the specified port and
|
||||
listens for client connections */
|
||||
server_transport =
|
||||
g_object_new (THRIFT_TYPE_SERVER_SOCKET,
|
||||
"port", 9090,
|
||||
NULL);
|
||||
|
||||
/* Create our transport factory, used by the server to wrap "raw"
|
||||
incoming connections from the client (in this case with a
|
||||
ThriftBufferedTransport to improve performance) */
|
||||
transport_factory =
|
||||
g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY,
|
||||
NULL);
|
||||
|
||||
/* Create our protocol factory, which determines which wire protocol
|
||||
the server will use (in this case, Thrift's binary protocol) */
|
||||
protocol_factory =
|
||||
g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY,
|
||||
NULL);
|
||||
|
||||
/* Create the server itself */
|
||||
server =
|
||||
g_object_new (THRIFT_TYPE_SIMPLE_SERVER,
|
||||
"processor", processor,
|
||||
"server_transport", server_transport,
|
||||
"input_transport_factory", transport_factory,
|
||||
"output_transport_factory", transport_factory,
|
||||
"input_protocol_factory", protocol_factory,
|
||||
"output_protocol_factory", protocol_factory,
|
||||
NULL);
|
||||
|
||||
/* Install our SIGINT handler, which handles Ctrl-C being pressed by
|
||||
stopping the server gracefully (not strictly necessary, but a
|
||||
nice touch) */
|
||||
memset (&sigint_action, 0, sizeof (sigint_action));
|
||||
sigint_action.sa_handler = sigint_handler;
|
||||
sigint_action.sa_flags = SA_RESETHAND;
|
||||
sigaction (SIGINT, &sigint_action, NULL);
|
||||
|
||||
/* Start the server, which will run until its stop method is invoked
|
||||
(from within the SIGINT handler, in this case) */
|
||||
puts ("Starting the server...");
|
||||
thrift_server_serve (server, &error);
|
||||
|
||||
/* If the server stopped for any reason other than having been
|
||||
interrupted by the user, report the error */
|
||||
if (!sigint_received) {
|
||||
g_message ("thrift_server_serve: %s",
|
||||
error != NULL ? error->message : "(null)");
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
puts ("done.");
|
||||
|
||||
g_object_unref (server);
|
||||
g_object_unref (transport_factory);
|
||||
g_object_unref (protocol_factory);
|
||||
g_object_unref (server_transport);
|
||||
|
||||
g_object_unref (processor);
|
||||
g_object_unref (handler);
|
||||
|
||||
return exit_status;
|
||||
}
|
|
@ -72,7 +72,7 @@ func NewEpoller() (*Epoller, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// Add creates a epoll console based on the provided console. The console will
|
||||
// Add creates an epoll console based on the provided console. The console will
|
||||
// be registered with EPOLLET (i.e. using edge-triggered notification) and its
|
||||
// file descriptor will be set to non-blocking mode. After this, user should use
|
||||
// the return console to perform I/O.
|
||||
|
@ -134,7 +134,7 @@ func (e *Epoller) Wait() error {
|
|||
}
|
||||
}
|
||||
|
||||
// Close unregister the console's file descriptor from epoll interface
|
||||
// CloseConsole unregisters the console's file descriptor from epoll interface
|
||||
func (e *Epoller) CloseConsole(fd int) error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
@ -149,12 +149,12 @@ func (e *Epoller) getConsole(sysfd int) *EpollConsole {
|
|||
return f
|
||||
}
|
||||
|
||||
// Close the epoll fd
|
||||
// Close closes the epoll fd
|
||||
func (e *Epoller) Close() error {
|
||||
return unix.Close(e.efd)
|
||||
}
|
||||
|
||||
// EpollConsole acts like a console but register its file descriptor with a
|
||||
// EpollConsole acts like a console but registers its file descriptor with an
|
||||
// epoll fd and uses epoll API to perform I/O.
|
||||
type EpollConsole struct {
|
||||
Console
|
||||
|
@ -167,7 +167,7 @@ type EpollConsole struct {
|
|||
// Read reads up to len(p) bytes into p. It returns the number of bytes read
|
||||
// (0 <= n <= len(p)) and any error encountered.
|
||||
//
|
||||
// If the console's read returns EAGAIN or EIO, we assumes that its a
|
||||
// If the console's read returns EAGAIN or EIO, we assume that it's a
|
||||
// temporary error because the other side went away and wait for the signal
|
||||
// generated by epoll event to continue.
|
||||
func (ec *EpollConsole) Read(p []byte) (n int, err error) {
|
||||
|
@ -207,7 +207,7 @@ func (ec *EpollConsole) Read(p []byte) (n int, err error) {
|
|||
// written from p (0 <= n <= len(p)) and any error encountered that caused
|
||||
// the write to stop early.
|
||||
//
|
||||
// If writes to the console returns EAGAIN or EIO, we assumes that its a
|
||||
// If writes to the console returns EAGAIN or EIO, we assume that it's a
|
||||
// temporary error because the other side went away and wait for the signal
|
||||
// generated by epoll event to continue.
|
||||
func (ec *EpollConsole) Write(p []byte) (n int, err error) {
|
||||
|
@ -224,7 +224,7 @@ func (ec *EpollConsole) Write(p []byte) (n int, err error) {
|
|||
} else {
|
||||
hangup = (err == unix.EAGAIN || err == unix.EIO)
|
||||
}
|
||||
// if the other end disappear, assume this is temporary and wait for the
|
||||
// if the other end disappears, assume this is temporary and wait for the
|
||||
// signal to continue again.
|
||||
if hangup {
|
||||
ec.writec.Wait()
|
||||
|
@ -242,7 +242,7 @@ func (ec *EpollConsole) Write(p []byte) (n int, err error) {
|
|||
return n, err
|
||||
}
|
||||
|
||||
// Close closed the file descriptor and signal call waiters for this fd.
|
||||
// Shutdown closes the file descriptor and signals call waiters for this fd.
|
||||
// It accepts a callback which will be called with the console's fd. The
|
||||
// callback typically will be used to do further cleanup such as unregister the
|
||||
// console's fd from the epoll interface.
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
@ -29,90 +28,55 @@ var (
|
|||
ErrNotImplemented = errors.New("not implemented")
|
||||
)
|
||||
|
||||
func (m *master) initStdios() {
|
||||
m.in = windows.Handle(os.Stdin.Fd())
|
||||
if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil {
|
||||
// Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
|
||||
if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
|
||||
vtInputSupported = true
|
||||
}
|
||||
// Unconditionally set the console mode back even on failure because SetConsoleMode
|
||||
// remembers invalid bits on input handles.
|
||||
windows.SetConsoleMode(m.in, m.inMode)
|
||||
} else {
|
||||
fmt.Printf("failed to get console mode for stdin: %v\n", err)
|
||||
}
|
||||
|
||||
m.out = windows.Handle(os.Stdout.Fd())
|
||||
if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil {
|
||||
if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
|
||||
m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
func (m *master) init() {
|
||||
m.h = windows.Handle(m.f.Fd())
|
||||
if err := windows.GetConsoleMode(m.h, &m.mode); err == nil {
|
||||
if m.f == os.Stdin {
|
||||
// Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
|
||||
if err = windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
|
||||
vtInputSupported = true
|
||||
}
|
||||
// Unconditionally set the console mode back even on failure because SetConsoleMode
|
||||
// remembers invalid bits on input handles.
|
||||
windows.SetConsoleMode(m.h, m.mode)
|
||||
} else if err := windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
|
||||
m.mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
} else {
|
||||
windows.SetConsoleMode(m.out, m.outMode)
|
||||
windows.SetConsoleMode(m.h, m.mode)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("failed to get console mode for stdout: %v\n", err)
|
||||
}
|
||||
|
||||
m.err = windows.Handle(os.Stderr.Fd())
|
||||
if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil {
|
||||
if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
|
||||
m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
} else {
|
||||
windows.SetConsoleMode(m.err, m.errMode)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("failed to get console mode for stderr: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
type master struct {
|
||||
in windows.Handle
|
||||
inMode uint32
|
||||
|
||||
out windows.Handle
|
||||
outMode uint32
|
||||
|
||||
err windows.Handle
|
||||
errMode uint32
|
||||
h windows.Handle
|
||||
mode uint32
|
||||
f *os.File
|
||||
}
|
||||
|
||||
func (m *master) SetRaw() error {
|
||||
if err := makeInputRaw(m.in, m.inMode); err != nil {
|
||||
return err
|
||||
if m.f == os.Stdin {
|
||||
if err := makeInputRaw(m.h, m.mode); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Set StdOut and StdErr to raw mode, we ignore failures since
|
||||
// windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of
|
||||
// Windows.
|
||||
windows.SetConsoleMode(m.h, m.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
|
||||
}
|
||||
|
||||
// Set StdOut and StdErr to raw mode, we ignore failures since
|
||||
// windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of
|
||||
// Windows.
|
||||
|
||||
windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
|
||||
|
||||
windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *master) Reset() error {
|
||||
for _, s := range []struct {
|
||||
fd windows.Handle
|
||||
mode uint32
|
||||
}{
|
||||
{m.in, m.inMode},
|
||||
{m.out, m.outMode},
|
||||
{m.err, m.errMode},
|
||||
} {
|
||||
if err := windows.SetConsoleMode(s.fd, s.mode); err != nil {
|
||||
return errors.Wrap(err, "unable to restore console mode")
|
||||
}
|
||||
if err := windows.SetConsoleMode(m.h, m.mode); err != nil {
|
||||
return errors.Wrap(err, "unable to restore console mode")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *master) Size() (WinSize, error) {
|
||||
var info windows.ConsoleScreenBufferInfo
|
||||
err := windows.GetConsoleScreenBufferInfo(m.out, &info)
|
||||
err := windows.GetConsoleScreenBufferInfo(m.h, &info)
|
||||
if err != nil {
|
||||
return WinSize{}, errors.Wrap(err, "unable to get console info")
|
||||
}
|
||||
|
@ -134,11 +98,11 @@ func (m *master) ResizeFrom(c Console) error {
|
|||
}
|
||||
|
||||
func (m *master) DisableEcho() error {
|
||||
mode := m.inMode &^ windows.ENABLE_ECHO_INPUT
|
||||
mode := m.mode &^ windows.ENABLE_ECHO_INPUT
|
||||
mode |= windows.ENABLE_PROCESSED_INPUT
|
||||
mode |= windows.ENABLE_LINE_INPUT
|
||||
|
||||
if err := windows.SetConsoleMode(m.in, mode); err != nil {
|
||||
if err := windows.SetConsoleMode(m.h, mode); err != nil {
|
||||
return errors.Wrap(err, "unable to set console to disable echo")
|
||||
}
|
||||
|
||||
|
@ -150,15 +114,15 @@ func (m *master) Close() error {
|
|||
}
|
||||
|
||||
func (m *master) Read(b []byte) (int, error) {
|
||||
panic("not implemented on windows")
|
||||
return m.f.Read(b)
|
||||
}
|
||||
|
||||
func (m *master) Write(b []byte) (int, error) {
|
||||
panic("not implemented on windows")
|
||||
return m.f.Write(b)
|
||||
}
|
||||
|
||||
func (m *master) Fd() uintptr {
|
||||
return uintptr(m.in)
|
||||
return uintptr(m.h)
|
||||
}
|
||||
|
||||
// on windows, console can only be made from os.Std{in,out,err}, hence there
|
||||
|
@ -210,7 +174,7 @@ func newMaster(f *os.File) (Console, error) {
|
|||
if f != os.Stdin && f != os.Stdout && f != os.Stderr {
|
||||
return nil, errors.New("creating a console from a file is not supported on windows")
|
||||
}
|
||||
m := &master{}
|
||||
m.initStdios()
|
||||
m := &master{f: f}
|
||||
m.init()
|
||||
return m, nil
|
||||
}
|
||||
|
|
|
@ -141,18 +141,8 @@ func openFifos(ctx context.Context, fifos *FIFOSet) (pipes, error) {
|
|||
// NewDirectIO returns an IO implementation that exposes the IO streams as io.ReadCloser
|
||||
// and io.WriteCloser.
|
||||
func NewDirectIO(ctx context.Context, fifos *FIFOSet) (*DirectIO, error) {
|
||||
return newDirectIO(ctx, fifos, false)
|
||||
}
|
||||
|
||||
// NewDirectIOWithTerminal returns an IO implementation that exposes the streams with terminal enabled
|
||||
func NewDirectIOWithTerminal(ctx context.Context, fifos *FIFOSet) (*DirectIO, error) {
|
||||
return newDirectIO(ctx, fifos, true)
|
||||
}
|
||||
|
||||
func newDirectIO(ctx context.Context, fifos *FIFOSet, terminal bool) (*DirectIO, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
pipes, err := openFifos(ctx, fifos)
|
||||
fifos.Config.Terminal = terminal
|
||||
return &DirectIO{
|
||||
pipes: pipes,
|
||||
cio: cio{
|
||||
|
|
|
@ -36,9 +36,9 @@ import (
|
|||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
protobuf "github.com/gogo/protobuf/types"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/identity"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -50,10 +50,9 @@ func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts {
|
|||
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
||||
var (
|
||||
desc = im.Target()
|
||||
id = desc.Digest
|
||||
store = client.ContentStore()
|
||||
)
|
||||
index, err := decodeIndex(ctx, store, id)
|
||||
index, err := decodeIndex(ctx, store, desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -80,7 +79,7 @@ func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts {
|
|||
}
|
||||
c.Image = index.Annotations["image.name"]
|
||||
case images.MediaTypeContainerd1CheckpointConfig:
|
||||
data, err := content.ReadBlob(ctx, store, m.Digest)
|
||||
data, err := content.ReadBlob(ctx, store, m)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to read checkpoint config")
|
||||
}
|
||||
|
@ -113,7 +112,7 @@ func WithTaskCheckpoint(im Image) NewTaskOpts {
|
|||
return func(ctx context.Context, c *Client, info *TaskInfo) error {
|
||||
desc := im.Target()
|
||||
id := desc.Digest
|
||||
index, err := decodeIndex(ctx, c.ContentStore(), id)
|
||||
index, err := decodeIndex(ctx, c.ContentStore(), desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -131,9 +130,9 @@ func WithTaskCheckpoint(im Image) NewTaskOpts {
|
|||
}
|
||||
}
|
||||
|
||||
func decodeIndex(ctx context.Context, store content.Provider, id digest.Digest) (*v1.Index, error) {
|
||||
func decodeIndex(ctx context.Context, store content.Provider, desc ocispec.Descriptor) (*v1.Index, error) {
|
||||
var index v1.Index
|
||||
p, err := content.ReadBlob(ctx, store, id)
|
||||
p, err := content.ReadBlob(ctx, store, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// ReaderAt extends the standard io.ReaderAt interface with reporting of Size and io.Closer
|
||||
|
@ -33,12 +34,16 @@ type ReaderAt interface {
|
|||
|
||||
// Provider provides a reader interface for specific content
|
||||
type Provider interface {
|
||||
ReaderAt(ctx context.Context, dgst digest.Digest) (ReaderAt, error)
|
||||
// ReaderAt only requires desc.Digest to be set.
|
||||
// Other fields in the descriptor may be used internally for resolving
|
||||
// the location of the actual data.
|
||||
ReaderAt(ctx context.Context, dec ocispec.Descriptor) (ReaderAt, error)
|
||||
}
|
||||
|
||||
// Ingester writes content
|
||||
type Ingester interface {
|
||||
Writer(ctx context.Context, ref string, size int64, expected digest.Digest) (Writer, error)
|
||||
// Some implementations require WithRef to be included in opts.
|
||||
Writer(ctx context.Context, opts ...WriterOpt) (Writer, error)
|
||||
}
|
||||
|
||||
// Info holds content specific information
|
||||
|
@ -142,3 +147,33 @@ func WithLabels(labels map[string]string) Opt {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WriterOpts is internally used by WriterOpt.
|
||||
type WriterOpts struct {
|
||||
Ref string
|
||||
Desc ocispec.Descriptor
|
||||
}
|
||||
|
||||
// WriterOpt is used for passing options to Ingester.Writer.
|
||||
type WriterOpt func(*WriterOpts) error
|
||||
|
||||
// WithDescriptor specifies an OCI descriptor.
|
||||
// Writer may optionally use the descriptor internally for resolving
|
||||
// the location of the actual data.
|
||||
// Write does not require any field of desc to be set.
|
||||
// If the data size is unknown, desc.Size should be set to 0.
|
||||
// Some implementations may also accept negative values as "unknown".
|
||||
func WithDescriptor(desc ocispec.Descriptor) WriterOpt {
|
||||
return func(opts *WriterOpts) error {
|
||||
opts.Desc = desc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithRef specifies a ref string.
|
||||
func WithRef(ref string) WriterOpt {
|
||||
return func(opts *WriterOpts) error {
|
||||
opts.Ref = ref
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -45,8 +46,8 @@ func NewReader(ra ReaderAt) io.Reader {
|
|||
// ReadBlob retrieves the entire contents of the blob from the provider.
|
||||
//
|
||||
// Avoid using this for large blobs, such as layers.
|
||||
func ReadBlob(ctx context.Context, provider Provider, dgst digest.Digest) ([]byte, error) {
|
||||
ra, err := provider.ReaderAt(ctx, dgst)
|
||||
func ReadBlob(ctx context.Context, provider Provider, desc ocispec.Descriptor) ([]byte, error) {
|
||||
ra, err := provider.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -65,8 +66,8 @@ func ReadBlob(ctx context.Context, provider Provider, dgst digest.Digest) ([]byt
|
|||
// This is useful when the digest and size are known beforehand.
|
||||
//
|
||||
// Copy is buffered, so no need to wrap reader in buffered io.
|
||||
func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, size int64, expected digest.Digest, opts ...Opt) error {
|
||||
cw, err := OpenWriter(ctx, cs, ref, size, expected)
|
||||
func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, desc ocispec.Descriptor, opts ...Opt) error {
|
||||
cw, err := OpenWriter(ctx, cs, WithRef(ref), WithDescriptor(desc))
|
||||
if err != nil {
|
||||
if !errdefs.IsAlreadyExists(err) {
|
||||
return err
|
||||
|
@ -76,19 +77,19 @@ func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, size i
|
|||
}
|
||||
defer cw.Close()
|
||||
|
||||
return Copy(ctx, cw, r, size, expected, opts...)
|
||||
return Copy(ctx, cw, r, desc.Size, desc.Digest, opts...)
|
||||
}
|
||||
|
||||
// OpenWriter opens a new writer for the given reference, retrying if the writer
|
||||
// is locked until the reference is available or returns an error.
|
||||
func OpenWriter(ctx context.Context, cs Ingester, ref string, size int64, expected digest.Digest) (Writer, error) {
|
||||
func OpenWriter(ctx context.Context, cs Ingester, opts ...WriterOpt) (Writer, error) {
|
||||
var (
|
||||
cw Writer
|
||||
err error
|
||||
retry = 16
|
||||
)
|
||||
for {
|
||||
cw, err = cs.Writer(ctx, ref, size, expected)
|
||||
cw, err = cs.Writer(ctx, opts...)
|
||||
if err != nil {
|
||||
if !errdefs.IsUnavailable(err) {
|
||||
return nil, err
|
||||
|
|
|
@ -34,6 +34,7 @@ import (
|
|||
"github.com/containerd/containerd/filters"
|
||||
"github.com/containerd/containerd/log"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -119,15 +120,15 @@ func (s *store) info(dgst digest.Digest, fi os.FileInfo, labels map[string]strin
|
|||
}
|
||||
|
||||
// ReaderAt returns an io.ReaderAt for the blob.
|
||||
func (s *store) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) {
|
||||
p := s.blobPath(dgst)
|
||||
func (s *store) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
|
||||
p := s.blobPath(desc.Digest)
|
||||
fi, err := os.Stat(p)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, errors.Wrapf(errdefs.ErrNotFound, "blob %s expected at %s", dgst, p)
|
||||
return nil, errors.Wrapf(errdefs.ErrNotFound, "blob %s expected at %s", desc.Digest, p)
|
||||
}
|
||||
|
||||
fp, err := os.Open(p)
|
||||
|
@ -136,7 +137,7 @@ func (s *store) ReaderAt(ctx context.Context, dgst digest.Digest) (content.Reade
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return nil, errors.Wrapf(errdefs.ErrNotFound, "blob %s expected at %s", dgst, p)
|
||||
return nil, errors.Wrapf(errdefs.ErrNotFound, "blob %s expected at %s", desc.Digest, p)
|
||||
}
|
||||
|
||||
return sizeReaderAt{size: fi.Size(), fp: fp}, nil
|
||||
|
@ -400,11 +401,22 @@ func (s *store) total(ingestPath string) int64 {
|
|||
// ref at a time.
|
||||
//
|
||||
// The argument `ref` is used to uniquely identify a long-lived writer transaction.
|
||||
func (s *store) Writer(ctx context.Context, ref string, total int64, expected digest.Digest) (content.Writer, error) {
|
||||
func (s *store) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
|
||||
var wOpts content.WriterOpts
|
||||
for _, opt := range opts {
|
||||
if err := opt(&wOpts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// TODO(AkihiroSuda): we could create a random string or one calculated based on the context
|
||||
// https://github.com/containerd/containerd/issues/2129#issuecomment-380255019
|
||||
if wOpts.Ref == "" {
|
||||
return nil, errors.Wrap(errdefs.ErrInvalidArgument, "ref must not be empty")
|
||||
}
|
||||
var lockErr error
|
||||
for count := uint64(0); count < 10; count++ {
|
||||
time.Sleep(time.Millisecond * time.Duration(rand.Intn(1<<count)))
|
||||
if err := tryLock(ref); err != nil {
|
||||
if err := tryLock(wOpts.Ref); err != nil {
|
||||
if !errdefs.IsUnavailable(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -420,9 +432,9 @@ func (s *store) Writer(ctx context.Context, ref string, total int64, expected di
|
|||
return nil, lockErr
|
||||
}
|
||||
|
||||
w, err := s.writer(ctx, ref, total, expected)
|
||||
w, err := s.writer(ctx, wOpts.Ref, wOpts.Desc.Size, wOpts.Desc.Digest)
|
||||
if err != nil {
|
||||
unlock(ref)
|
||||
unlock(wOpts.Ref)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/containerd/containerd/errdefs"
|
||||
protobuftypes "github.com/gogo/protobuf/types"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type proxyContentStore struct {
|
||||
|
@ -88,15 +89,16 @@ func (pcs *proxyContentStore) Delete(ctx context.Context, dgst digest.Digest) er
|
|||
return nil
|
||||
}
|
||||
|
||||
func (pcs *proxyContentStore) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) {
|
||||
i, err := pcs.Info(ctx, dgst)
|
||||
// ReaderAt ignores MediaType.
|
||||
func (pcs *proxyContentStore) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
|
||||
i, err := pcs.Info(ctx, desc.Digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &remoteReaderAt{
|
||||
ctx: ctx,
|
||||
digest: dgst,
|
||||
digest: desc.Digest,
|
||||
size: i.Size,
|
||||
client: pcs.client,
|
||||
}, nil
|
||||
|
@ -157,14 +159,21 @@ func (pcs *proxyContentStore) ListStatuses(ctx context.Context, filters ...strin
|
|||
return statuses, nil
|
||||
}
|
||||
|
||||
func (pcs *proxyContentStore) Writer(ctx context.Context, ref string, size int64, expected digest.Digest) (content.Writer, error) {
|
||||
wrclient, offset, err := pcs.negotiate(ctx, ref, size, expected)
|
||||
// Writer ignores MediaType.
|
||||
func (pcs *proxyContentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
|
||||
var wOpts content.WriterOpts
|
||||
for _, opt := range opts {
|
||||
if err := opt(&wOpts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
wrclient, offset, err := pcs.negotiate(ctx, wOpts.Ref, wOpts.Desc.Size, wOpts.Desc.Digest)
|
||||
if err != nil {
|
||||
return nil, errdefs.FromGRPC(err)
|
||||
}
|
||||
|
||||
return &remoteWriter{
|
||||
ref: ref,
|
||||
ref: wOpts.Ref,
|
||||
client: wrclient,
|
||||
offset: offset,
|
||||
}, nil
|
||||
|
|
|
@ -73,7 +73,7 @@ func (s *fsApplier) Apply(ctx context.Context, desc ocispec.Descriptor, mounts [
|
|||
|
||||
var ocidesc ocispec.Descriptor
|
||||
if err := mount.WithTempMount(ctx, mounts, func(root string) error {
|
||||
ra, err := s.store.ReaderAt(ctx, desc.Digest)
|
||||
ra, err := s.store.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get reader from content store")
|
||||
}
|
||||
|
|
|
@ -86,7 +86,11 @@ func (s *walkingDiff) Compare(ctx context.Context, lower, upper []mount.Mount, o
|
|||
config.Reference = uniqueRef()
|
||||
}
|
||||
|
||||
cw, err := s.store.Writer(ctx, config.Reference, 0, "")
|
||||
cw, err := s.store.Writer(ctx,
|
||||
content.WithRef(config.Reference),
|
||||
content.WithDescriptor(ocispec.Descriptor{
|
||||
MediaType: config.MediaType, // most contentstore implementations just ignore this
|
||||
}))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to open writer")
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc
|
|||
if err := Walk(ctx, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
||||
switch desc.MediaType {
|
||||
case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
|
||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc
|
|||
}
|
||||
|
||||
if desc.Platform == nil {
|
||||
p, err := content.ReadBlob(ctx, provider, manifest.Config.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, manifest.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc
|
|||
|
||||
return nil, nil
|
||||
case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
|
||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ func Platforms(ctx context.Context, provider content.Provider, image ocispec.Des
|
|||
|
||||
switch desc.MediaType {
|
||||
case MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
|
||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ func Check(ctx context.Context, provider content.Provider, image ocispec.Descrip
|
|||
required = append([]ocispec.Descriptor{mfst.Config}, mfst.Layers...)
|
||||
|
||||
for _, desc := range required {
|
||||
ra, err := provider.ReaderAt(ctx, desc.Digest)
|
||||
ra, err := provider.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
missing = append(missing, desc)
|
||||
|
@ -305,7 +305,7 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
|
|||
var descs []ocispec.Descriptor
|
||||
switch desc.MediaType {
|
||||
case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
|
||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -320,7 +320,7 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
|
|||
descs = append(descs, manifest.Config)
|
||||
descs = append(descs, manifest.Layers...)
|
||||
case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
|
||||
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -351,7 +351,7 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
|
|||
// These are used to verify that a set of layers unpacked to the expected
|
||||
// values.
|
||||
func RootFS(ctx context.Context, provider content.Provider, configDesc ocispec.Descriptor) ([]digest.Digest, error) {
|
||||
p, err := content.ReadBlob(ctx, provider, configDesc.Digest)
|
||||
p, err := content.ReadBlob(ctx, provider, configDesc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ func blobRecord(cs content.Provider, desc ocispec.Descriptor) tarRecord {
|
|||
Typeflag: tar.TypeReg,
|
||||
},
|
||||
CopyTo: func(ctx context.Context, w io.Writer) (int64, error) {
|
||||
r, err := cs.ReaderAt(ctx, desc.Digest)
|
||||
r, err := cs.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ func onUntarBlob(ctx context.Context, r io.Reader, store content.Ingester, name
|
|||
return errors.Errorf("unsupported algorithm: %s", algo)
|
||||
}
|
||||
dgst := digest.NewDigestFromHex(algo.String(), split[2])
|
||||
return content.WriteBlob(ctx, store, "unknown-"+dgst.String(), r, size, dgst)
|
||||
return content.WriteBlob(ctx, store, "unknown-"+dgst.String(), r, ocispec.Descriptor{Size: size, Digest: dgst})
|
||||
}
|
||||
|
||||
// GetChildrenDescriptors returns children blob descriptors for the following supported types:
|
||||
|
@ -175,7 +175,7 @@ func setGCRefContentLabels(ctx context.Context, store content.Store, desc ocispe
|
|||
}
|
||||
return err
|
||||
}
|
||||
ra, err := store.ReaderAt(ctx, desc.Digest)
|
||||
ra, err := store.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import (
|
|||
"github.com/containerd/containerd/metadata/boltutil"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -337,7 +338,18 @@ func (cs *contentStore) Abort(ctx context.Context, ref string) error {
|
|||
|
||||
}
|
||||
|
||||
func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expected digest.Digest) (content.Writer, error) {
|
||||
func (cs *contentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
|
||||
var wOpts content.WriterOpts
|
||||
for _, opt := range opts {
|
||||
if err := opt(&wOpts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// TODO(AkihiroSuda): we could create a random string or one calculated based on the context
|
||||
// https://github.com/containerd/containerd/issues/2129#issuecomment-380255019
|
||||
if wOpts.Ref == "" {
|
||||
return nil, errors.Wrap(errdefs.ErrInvalidArgument, "ref must not be empty")
|
||||
}
|
||||
ns, err := namespaces.NamespaceRequired(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -353,12 +365,12 @@ func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expe
|
|||
)
|
||||
if err := update(ctx, cs.db, func(tx *bolt.Tx) error {
|
||||
var shared bool
|
||||
if expected != "" {
|
||||
cbkt := getBlobBucket(tx, ns, expected)
|
||||
if wOpts.Desc.Digest != "" {
|
||||
cbkt := getBlobBucket(tx, ns, wOpts.Desc.Digest)
|
||||
if cbkt != nil {
|
||||
// Add content to lease to prevent other reference removals
|
||||
// from effecting this object during a provided lease
|
||||
if err := addContentLease(ctx, tx, expected); err != nil {
|
||||
if err := addContentLease(ctx, tx, wOpts.Desc.Digest); err != nil {
|
||||
return errors.Wrap(err, "unable to lease content")
|
||||
}
|
||||
// Return error outside of transaction to ensure
|
||||
|
@ -367,18 +379,18 @@ func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expe
|
|||
return nil
|
||||
}
|
||||
|
||||
if st, err := cs.Store.Info(ctx, expected); err == nil {
|
||||
if st, err := cs.Store.Info(ctx, wOpts.Desc.Digest); err == nil {
|
||||
// Ensure the expected size is the same, it is likely
|
||||
// an error if the size is mismatched but the caller
|
||||
// must resolve this on commit
|
||||
if size == 0 || size == st.Size {
|
||||
if wOpts.Desc.Size == 0 || wOpts.Desc.Size == st.Size {
|
||||
shared = true
|
||||
size = st.Size
|
||||
wOpts.Desc.Size = st.Size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bkt, err := createIngestBucket(tx, ns, ref)
|
||||
bkt, err := createIngestBucket(tx, ns, wOpts.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -390,7 +402,7 @@ func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expe
|
|||
return err
|
||||
}
|
||||
|
||||
bref = createKey(sid, ns, ref)
|
||||
bref = createKey(sid, ns, wOpts.Ref)
|
||||
if err := bkt.Put(bucketKeyRef, []byte(bref)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -399,7 +411,7 @@ func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expe
|
|||
}
|
||||
|
||||
if shared {
|
||||
if err := bkt.Put(bucketKeyExpected, []byte(expected)); err != nil {
|
||||
if err := bkt.Put(bucketKeyExpected, []byte(wOpts.Desc.Digest)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
@ -407,19 +419,21 @@ func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expe
|
|||
// already checked against the user metadata. The content must
|
||||
// be committed in the namespace before it will be seen as
|
||||
// available in the current namespace.
|
||||
w, err = cs.Store.Writer(ctx, bref, size, "")
|
||||
desc := wOpts.Desc
|
||||
desc.Digest = ""
|
||||
w, err = cs.Store.Writer(ctx, content.WithRef(bref), content.WithDescriptor(desc))
|
||||
}
|
||||
return err
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exists {
|
||||
return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", expected)
|
||||
return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", wOpts.Desc.Digest)
|
||||
}
|
||||
|
||||
return &namespacedWriter{
|
||||
ctx: ctx,
|
||||
ref: ref,
|
||||
ref: wOpts.Ref,
|
||||
namespace: ns,
|
||||
db: cs.db,
|
||||
provider: cs.Store,
|
||||
|
@ -427,8 +441,7 @@ func (cs *contentStore) Writer(ctx context.Context, ref string, size int64, expe
|
|||
w: w,
|
||||
bref: bref,
|
||||
started: time.Now(),
|
||||
expected: expected,
|
||||
size: size,
|
||||
desc: wOpts.Desc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -445,10 +458,9 @@ type namespacedWriter struct {
|
|||
|
||||
w content.Writer
|
||||
|
||||
bref string
|
||||
started time.Time
|
||||
expected digest.Digest
|
||||
size int64
|
||||
bref string
|
||||
started time.Time
|
||||
desc ocispec.Descriptor
|
||||
}
|
||||
|
||||
func (nw *namespacedWriter) Close() error {
|
||||
|
@ -465,7 +477,7 @@ func (nw *namespacedWriter) Write(p []byte) (int, error) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
if err := nw.createAndCopy(nw.ctx, nw.size); err != nil {
|
||||
if err := nw.createAndCopy(nw.ctx, nw.desc); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
@ -477,31 +489,35 @@ func (nw *namespacedWriter) Digest() digest.Digest {
|
|||
if nw.w != nil {
|
||||
return nw.w.Digest()
|
||||
}
|
||||
return nw.expected
|
||||
return nw.desc.Digest
|
||||
}
|
||||
|
||||
func (nw *namespacedWriter) Truncate(size int64) error {
|
||||
if nw.w != nil {
|
||||
return nw.w.Truncate(size)
|
||||
}
|
||||
|
||||
return nw.createAndCopy(nw.ctx, size)
|
||||
desc := nw.desc
|
||||
desc.Size = size
|
||||
desc.Digest = ""
|
||||
return nw.createAndCopy(nw.ctx, desc)
|
||||
}
|
||||
|
||||
func (nw *namespacedWriter) createAndCopy(ctx context.Context, size int64) error {
|
||||
w, err := nw.provider.Writer(ctx, nw.bref, nw.size, "")
|
||||
func (nw *namespacedWriter) createAndCopy(ctx context.Context, desc ocispec.Descriptor) error {
|
||||
nwDescWithoutDigest := desc
|
||||
nwDescWithoutDigest.Digest = ""
|
||||
w, err := nw.provider.Writer(ctx, content.WithRef(nw.bref), content.WithDescriptor(nwDescWithoutDigest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if size > 0 {
|
||||
ra, err := nw.provider.ReaderAt(ctx, nw.expected)
|
||||
if desc.Size > 0 {
|
||||
ra, err := nw.provider.ReaderAt(ctx, nw.desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ra.Close()
|
||||
|
||||
if err := content.CopyReaderAt(w, ra, size); err != nil {
|
||||
if err := content.CopyReaderAt(w, ra, desc.Size); err != nil {
|
||||
nw.w.Close()
|
||||
nw.w = nil
|
||||
return err
|
||||
|
@ -544,14 +560,14 @@ func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64,
|
|||
|
||||
var actual digest.Digest
|
||||
if nw.w == nil {
|
||||
if size != 0 && size != nw.size {
|
||||
return "", errors.Errorf("%q failed size validation: %v != %v", nw.ref, nw.size, size)
|
||||
if size != 0 && size != nw.desc.Size {
|
||||
return "", errors.Errorf("%q failed size validation: %v != %v", nw.ref, nw.desc.Size, size)
|
||||
}
|
||||
if expected != "" && expected != nw.expected {
|
||||
if expected != "" && expected != nw.desc.Digest {
|
||||
return "", errors.Errorf("%q unexpected digest", nw.ref)
|
||||
}
|
||||
size = nw.size
|
||||
actual = nw.expected
|
||||
size = nw.desc.Size
|
||||
actual = nw.desc.Digest
|
||||
if getBlobBucket(tx, nw.namespace, actual) != nil {
|
||||
return "", errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", actual)
|
||||
}
|
||||
|
@ -601,11 +617,11 @@ func (nw *namespacedWriter) Status() (st content.Status, err error) {
|
|||
if nw.w != nil {
|
||||
st, err = nw.w.Status()
|
||||
} else {
|
||||
st.Offset = nw.size
|
||||
st.Total = nw.size
|
||||
st.Offset = nw.desc.Size
|
||||
st.Total = nw.desc.Size
|
||||
st.StartedAt = nw.started
|
||||
st.UpdatedAt = nw.started
|
||||
st.Expected = nw.expected
|
||||
st.Expected = nw.desc.Digest
|
||||
}
|
||||
if err == nil {
|
||||
st.Ref = nw.ref
|
||||
|
@ -613,11 +629,11 @@ func (nw *namespacedWriter) Status() (st content.Status, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (cs *contentStore) ReaderAt(ctx context.Context, dgst digest.Digest) (content.ReaderAt, error) {
|
||||
if err := cs.checkAccess(ctx, dgst); err != nil {
|
||||
func (cs *contentStore) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
|
||||
if err := cs.checkAccess(ctx, desc.Digest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cs.Store.ReaderAt(ctx, dgst)
|
||||
return cs.Store.ReaderAt(ctx, desc)
|
||||
}
|
||||
|
||||
func (cs *contentStore) checkAccess(ctx context.Context, dgst digest.Digest) error {
|
||||
|
|
|
@ -117,7 +117,7 @@ func WithImageConfig(image Image) SpecOpts {
|
|||
)
|
||||
switch ic.MediaType {
|
||||
case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
|
||||
p, err := content.ReadBlob(ctx, image.ContentStore(), ic.Digest)
|
||||
p, err := content.ReadBlob(ctx, image.ContentStore(), ic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ func WithImageConfig(image Image) SpecOpts {
|
|||
)
|
||||
switch ic.MediaType {
|
||||
case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
|
||||
p, err := content.ReadBlob(ctx, image.ContentStore(), ic.Digest)
|
||||
p, err := content.ReadBlob(ctx, image.ContentStore(), ic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -155,9 +155,18 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten
|
|||
location := resp.Header.Get("Location")
|
||||
// Support paths without host in location
|
||||
if strings.HasPrefix(location, "/") {
|
||||
u := p.base
|
||||
u.Path = location
|
||||
location = u.String()
|
||||
// Support location string containing path and query
|
||||
qmIndex := strings.Index(location, "?")
|
||||
if qmIndex > 0 {
|
||||
u := p.base
|
||||
u.Path = location[:qmIndex]
|
||||
u.RawQuery = location[qmIndex+1:]
|
||||
location = u.String()
|
||||
} else {
|
||||
u := p.base
|
||||
u.Path = location
|
||||
location = u.String()
|
||||
}
|
||||
}
|
||||
|
||||
req, err = http.NewRequest(http.MethodPut, location, nil)
|
||||
|
|
|
@ -211,12 +211,12 @@ func (c *Converter) Convert(ctx context.Context, opts ...ConvertOpt) (ocispec.De
|
|||
}
|
||||
|
||||
ref := remotes.MakeRefKey(ctx, desc)
|
||||
if err := content.WriteBlob(ctx, c.contentStore, ref, bytes.NewReader(mb), desc.Size, desc.Digest, content.WithLabels(labels)); err != nil {
|
||||
if err := content.WriteBlob(ctx, c.contentStore, ref, bytes.NewReader(mb), desc, content.WithLabels(labels)); err != nil {
|
||||
return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config")
|
||||
}
|
||||
|
||||
ref = remotes.MakeRefKey(ctx, config)
|
||||
if err := content.WriteBlob(ctx, c.contentStore, ref, bytes.NewReader(b), config.Size, config.Digest); err != nil {
|
||||
if err := content.WriteBlob(ctx, c.contentStore, ref, bytes.NewReader(b), config); err != nil {
|
||||
return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config")
|
||||
}
|
||||
|
||||
|
@ -257,15 +257,15 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
|
|||
var (
|
||||
ref = remotes.MakeRefKey(ctx, desc)
|
||||
calc = newBlobStateCalculator()
|
||||
size = desc.Size
|
||||
)
|
||||
|
||||
// size may be unknown, set to zero for content ingest
|
||||
if size == -1 {
|
||||
size = 0
|
||||
ingestDesc := desc
|
||||
if ingestDesc.Size == -1 {
|
||||
ingestDesc.Size = 0
|
||||
}
|
||||
|
||||
cw, err := content.OpenWriter(ctx, c.contentStore, ref, size, desc.Digest)
|
||||
cw, err := content.OpenWriter(ctx, c.contentStore, content.WithRef(ref), content.WithDescriptor(ingestDesc))
|
||||
if err != nil {
|
||||
if !errdefs.IsAlreadyExists(err) {
|
||||
return err
|
||||
|
@ -274,7 +274,7 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
|
|||
// TODO: Check if blob -> diff id mapping already exists
|
||||
// TODO: Check if blob empty label exists
|
||||
|
||||
ra, err := c.contentStore.ReaderAt(ctx, desc.Digest)
|
||||
ra, err := c.contentStore.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -317,7 +317,7 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
|
|||
eg.Go(func() error {
|
||||
defer pw.Close()
|
||||
|
||||
return content.Copy(ctx, cw, io.TeeReader(rc, pw), size, desc.Digest)
|
||||
return content.Copy(ctx, cw, io.TeeReader(rc, pw), ingestDesc.Size, ingestDesc.Digest)
|
||||
})
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
|
|
|
@ -81,7 +81,7 @@ func FetchHandler(ingester content.Ingester, fetcher Fetcher) images.HandlerFunc
|
|||
func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc ocispec.Descriptor) error {
|
||||
log.G(ctx).Debug("fetch")
|
||||
|
||||
cw, err := content.OpenWriter(ctx, ingester, MakeRefKey(ctx, desc), desc.Size, desc.Digest)
|
||||
cw, err := content.OpenWriter(ctx, ingester, content.WithRef(MakeRefKey(ctx, desc)), content.WithDescriptor(desc))
|
||||
if err != nil {
|
||||
if errdefs.IsAlreadyExists(err) {
|
||||
return nil
|
||||
|
@ -141,7 +141,7 @@ func push(ctx context.Context, provider content.Provider, pusher Pusher, desc oc
|
|||
}
|
||||
defer cw.Close()
|
||||
|
||||
ra, err := provider.ReaderAt(ctx, desc.Digest)
|
||||
ra, err := provider.ReaderAt(ctx, desc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -597,7 +597,7 @@ func (t *task) writeIndex(ctx context.Context, index *v1.Index) (d v1.Descriptor
|
|||
}
|
||||
|
||||
func writeContent(ctx context.Context, store content.Ingester, mediaType, ref string, r io.Reader, opts ...content.Opt) (d v1.Descriptor, err error) {
|
||||
writer, err := store.Writer(ctx, ref, 0, "")
|
||||
writer, err := store.Writer(ctx, content.WithRef(ref))
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
github.com/containerd/go-runc f271fa2021de855d4d918dbef83c5fe19db1bdd5
|
||||
github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925
|
||||
github.com/containerd/console 9290d21dc56074581f619579c43d970b4514bc08
|
||||
github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130
|
||||
github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788
|
||||
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
|
||||
|
@ -43,8 +43,7 @@ github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
|
|||
github.com/gotestyourself/gotestyourself 44dbf532bbf5767611f6f2a61bded572e337010a
|
||||
github.com/google/go-cmp v0.1.0
|
||||
|
||||
# cri dependencies
|
||||
github.com/containerd/cri b68fb075d49aa1c2885f45f2467142666c244f4a
|
||||
github.com/containerd/cri 8bcb9a95394e8d7845da1d6a994d3ac2a86d22f0
|
||||
github.com/containerd/go-cni f2d7272f12d045b16ed924f50e91f9f9cecc55a7
|
||||
github.com/blang/semver v3.1.0
|
||||
github.com/containernetworking/cni v0.6.0
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
The `contrib` directory contains scripts, images, and other helpful things
|
||||
which are not part of the core docker distribution. Please note that they
|
||||
could be out of date, since they do not receive the same attention as the
|
||||
rest of the repository.
|
|
@ -0,0 +1,10 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("EUID=%d\n", geteuid());
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int err = acct("/tmp/t");
|
||||
if (err == -1) {
|
||||
fprintf(stderr, "acct failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
.globl _start
|
||||
.text
|
||||
_start:
|
||||
xorl %eax, %eax
|
||||
incl %eax
|
||||
movb $0, %bl
|
||||
int $0x80
|
|
@ -0,0 +1,63 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define STACK_SIZE (1024 * 1024) /* Stack size for cloned child */
|
||||
|
||||
struct clone_args {
|
||||
char **argv;
|
||||
};
|
||||
|
||||
// child_exec is the func that will be executed as the result of clone
|
||||
static int child_exec(void *stuff)
|
||||
{
|
||||
struct clone_args *args = (struct clone_args *)stuff;
|
||||
if (execvp(args->argv[0], args->argv) != 0) {
|
||||
fprintf(stderr, "failed to execvp arguments %s\n",
|
||||
strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
// we should never reach here!
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct clone_args args;
|
||||
args.argv = &argv[1];
|
||||
|
||||
int clone_flags = CLONE_NEWNS | CLONE_NEWPID | SIGCHLD;
|
||||
|
||||
// allocate stack for child
|
||||
char *stack; /* Start of stack buffer */
|
||||
char *child_stack; /* End of stack buffer */
|
||||
stack =
|
||||
mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_ANON | MAP_STACK, -1, 0);
|
||||
if (stack == MAP_FAILED) {
|
||||
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
child_stack = stack + STACK_SIZE; /* Assume stack grows downward */
|
||||
|
||||
// the result of this call is that our child_exec will be run in another
|
||||
// process returning its pid
|
||||
pid_t pid = clone(child_exec, child_stack, clone_flags, &args);
|
||||
if (pid < 0) {
|
||||
fprintf(stderr, "clone failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
// lets wait on our child process here before we, the parent, exits
|
||||
if (waitpid(pid, NULL, 0) == -1) {
|
||||
fprintf(stderr, "failed to wait pid %d\n", pid);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
int main() {
|
||||
if (socket(PF_INET, SOCK_RAW, IPPROTO_UDP) == -1) {
|
||||
perror("socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
if (setgid(1) == -1) {
|
||||
perror("setgid");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
if (setuid(1) == -1) {
|
||||
perror("setuid");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
int main() {
|
||||
int s;
|
||||
struct sockaddr_in sin;
|
||||
|
||||
s = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (s == -1) {
|
||||
perror("socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = INADDR_ANY;
|
||||
sin.sin_port = htons(80);
|
||||
|
||||
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
|
||||
perror("bind");
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(s);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define STACK_SIZE (1024 * 1024) /* Stack size for cloned child */
|
||||
|
||||
struct clone_args {
|
||||
char **argv;
|
||||
};
|
||||
|
||||
// child_exec is the func that will be executed as the result of clone
|
||||
static int child_exec(void *stuff)
|
||||
{
|
||||
struct clone_args *args = (struct clone_args *)stuff;
|
||||
if (execvp(args->argv[0], args->argv) != 0) {
|
||||
fprintf(stderr, "failed to execvp arguments %s\n",
|
||||
strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
// we should never reach here!
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct clone_args args;
|
||||
args.argv = &argv[1];
|
||||
|
||||
int clone_flags = CLONE_NEWUSER | SIGCHLD;
|
||||
|
||||
// allocate stack for child
|
||||
char *stack; /* Start of stack buffer */
|
||||
char *child_stack; /* End of stack buffer */
|
||||
stack =
|
||||
mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_ANON | MAP_STACK, -1, 0);
|
||||
if (stack == MAP_FAILED) {
|
||||
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
child_stack = stack + STACK_SIZE; /* Assume stack grows downward */
|
||||
|
||||
// the result of this call is that our child_exec will be run in another
|
||||
// process returning its pid
|
||||
pid_t pid = clone(child_exec, child_stack, clone_flags, &args);
|
||||
if (pid < 0) {
|
||||
fprintf(stderr, "clone failed: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
// lets wait on our child process here before we, the parent, exits
|
||||
if (waitpid(pid, NULL, 0) == -1) {
|
||||
fprintf(stderr, "failed to wait pid %d\n", pid);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package svc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// event represents auto-reset, initially non-signaled Windows event.
|
||||
// It is used to communicate between go and asm parts of this package.
|
||||
type event struct {
|
||||
h windows.Handle
|
||||
}
|
||||
|
||||
func newEvent() (*event, error) {
|
||||
h, err := windows.CreateEvent(nil, 0, 0, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &event{h: h}, nil
|
||||
}
|
||||
|
||||
func (e *event) Close() error {
|
||||
return windows.CloseHandle(e.h)
|
||||
}
|
||||
|
||||
func (e *event) Set() error {
|
||||
return windows.SetEvent(e.h)
|
||||
}
|
||||
|
||||
func (e *event) Wait() error {
|
||||
s, err := windows.WaitForSingleObject(e.h, windows.INFINITE)
|
||||
switch s {
|
||||
case windows.WAIT_OBJECT_0:
|
||||
break
|
||||
case windows.WAIT_FAILED:
|
||||
return err
|
||||
default:
|
||||
return errors.New("unexpected result from WaitForSingleObject")
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
// +build !go1.3
|
||||
|
||||
// copied from pkg/runtime
|
||||
typedef unsigned int uint32;
|
||||
typedef unsigned long long int uint64;
|
||||
#ifdef _64BIT
|
||||
typedef uint64 uintptr;
|
||||
#else
|
||||
typedef uint32 uintptr;
|
||||
#endif
|
||||
|
||||
// from sys_386.s or sys_amd64.s
|
||||
void ·servicemain(void);
|
||||
|
||||
void
|
||||
·getServiceMain(uintptr *r)
|
||||
{
|
||||
*r = (uintptr)·servicemain;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
// +build !go1.3
|
||||
|
||||
package svc
|
||||
|
||||
// from go12.c
|
||||
func getServiceMain(r *uintptr)
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
// +build go1.3
|
||||
|
||||
package svc
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
|
||||
|
||||
// Should be a built-in for unsafe.Pointer?
|
||||
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(p) + x)
|
||||
}
|
||||
|
||||
// funcPC returns the entry PC of the function f.
|
||||
// It assumes that f is a func value. Otherwise the behavior is undefined.
|
||||
func funcPC(f interface{}) uintptr {
|
||||
return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize))
|
||||
}
|
||||
|
||||
// from sys_386.s and sys_amd64.s
|
||||
func servicectlhandler(ctl uint32) uintptr
|
||||
func servicemain(argc uint32, argv **uint16)
|
||||
|
||||
func getServiceMain(r *uintptr) {
|
||||
*r = funcPC(servicemain)
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package svc
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func allocSid(subAuth0 uint32) (*windows.SID, error) {
|
||||
var sid *windows.SID
|
||||
err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY,
|
||||
1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sid, nil
|
||||
}
|
||||
|
||||
// IsAnInteractiveSession determines if calling process is running interactively.
|
||||
// It queries the process token for membership in the Interactive group.
|
||||
// http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s
|
||||
func IsAnInteractiveSession() (bool, error) {
|
||||
interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer windows.FreeSid(interSid)
|
||||
|
||||
serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer windows.FreeSid(serviceSid)
|
||||
|
||||
t, err := windows.OpenCurrentProcessToken()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer t.Close()
|
||||
|
||||
gs, err := t.GetTokenGroups()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
p := unsafe.Pointer(&gs.Groups[0])
|
||||
groups := (*[2 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount]
|
||||
for _, g := range groups {
|
||||
if windows.EqualSid(g.Sid, interSid) {
|
||||
return true, nil
|
||||
}
|
||||
if windows.EqualSid(g.Sid, serviceSid) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
|
@ -0,0 +1,363 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
// Package svc provides everything required to build Windows service.
|
||||
//
|
||||
package svc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// State describes service execution state (Stopped, Running and so on).
|
||||
type State uint32
|
||||
|
||||
const (
|
||||
Stopped = State(windows.SERVICE_STOPPED)
|
||||
StartPending = State(windows.SERVICE_START_PENDING)
|
||||
StopPending = State(windows.SERVICE_STOP_PENDING)
|
||||
Running = State(windows.SERVICE_RUNNING)
|
||||
ContinuePending = State(windows.SERVICE_CONTINUE_PENDING)
|
||||
PausePending = State(windows.SERVICE_PAUSE_PENDING)
|
||||
Paused = State(windows.SERVICE_PAUSED)
|
||||
)
|
||||
|
||||
// Cmd represents service state change request. It is sent to a service
|
||||
// by the service manager, and should be actioned upon by the service.
|
||||
type Cmd uint32
|
||||
|
||||
const (
|
||||
Stop = Cmd(windows.SERVICE_CONTROL_STOP)
|
||||
Pause = Cmd(windows.SERVICE_CONTROL_PAUSE)
|
||||
Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE)
|
||||
Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE)
|
||||
Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN)
|
||||
ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE)
|
||||
NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD)
|
||||
NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE)
|
||||
NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE)
|
||||
NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE)
|
||||
DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT)
|
||||
HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE)
|
||||
PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT)
|
||||
SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE)
|
||||
)
|
||||
|
||||
// Accepted is used to describe commands accepted by the service.
|
||||
// Note that Interrogate is always accepted.
|
||||
type Accepted uint32
|
||||
|
||||
const (
|
||||
AcceptStop = Accepted(windows.SERVICE_ACCEPT_STOP)
|
||||
AcceptShutdown = Accepted(windows.SERVICE_ACCEPT_SHUTDOWN)
|
||||
AcceptPauseAndContinue = Accepted(windows.SERVICE_ACCEPT_PAUSE_CONTINUE)
|
||||
AcceptParamChange = Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)
|
||||
AcceptNetBindChange = Accepted(windows.SERVICE_ACCEPT_NETBINDCHANGE)
|
||||
AcceptHardwareProfileChange = Accepted(windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
|
||||
AcceptPowerEvent = Accepted(windows.SERVICE_ACCEPT_POWEREVENT)
|
||||
AcceptSessionChange = Accepted(windows.SERVICE_ACCEPT_SESSIONCHANGE)
|
||||
)
|
||||
|
||||
// Status combines State and Accepted commands to fully describe running service.
|
||||
type Status struct {
|
||||
State State
|
||||
Accepts Accepted
|
||||
CheckPoint uint32 // used to report progress during a lengthy operation
|
||||
WaitHint uint32 // estimated time required for a pending operation, in milliseconds
|
||||
}
|
||||
|
||||
// ChangeRequest is sent to the service Handler to request service status change.
|
||||
type ChangeRequest struct {
|
||||
Cmd Cmd
|
||||
EventType uint32
|
||||
EventData uintptr
|
||||
CurrentStatus Status
|
||||
}
|
||||
|
||||
// Handler is the interface that must be implemented to build Windows service.
|
||||
type Handler interface {
|
||||
|
||||
// Execute will be called by the package code at the start of
|
||||
// the service, and the service will exit once Execute completes.
|
||||
// Inside Execute you must read service change requests from r and
|
||||
// act accordingly. You must keep service control manager up to date
|
||||
// about state of your service by writing into s as required.
|
||||
// args contains service name followed by argument strings passed
|
||||
// to the service.
|
||||
// You can provide service exit code in exitCode return parameter,
|
||||
// with 0 being "no error". You can also indicate if exit code,
|
||||
// if any, is service specific or not by using svcSpecificEC
|
||||
// parameter.
|
||||
Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32)
|
||||
}
|
||||
|
||||
var (
|
||||
// These are used by asm code.
|
||||
goWaitsH uintptr
|
||||
cWaitsH uintptr
|
||||
ssHandle uintptr
|
||||
sName *uint16
|
||||
sArgc uintptr
|
||||
sArgv **uint16
|
||||
ctlHandlerExProc uintptr
|
||||
cSetEvent uintptr
|
||||
cWaitForSingleObject uintptr
|
||||
cRegisterServiceCtrlHandlerExW uintptr
|
||||
)
|
||||
|
||||
func init() {
|
||||
k := syscall.MustLoadDLL("kernel32.dll")
|
||||
cSetEvent = k.MustFindProc("SetEvent").Addr()
|
||||
cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr()
|
||||
a := syscall.MustLoadDLL("advapi32.dll")
|
||||
cRegisterServiceCtrlHandlerExW = a.MustFindProc("RegisterServiceCtrlHandlerExW").Addr()
|
||||
}
|
||||
|
||||
// The HandlerEx prototype also has a context pointer but since we don't use
|
||||
// it at start-up time we don't have to pass it over either.
|
||||
type ctlEvent struct {
|
||||
cmd Cmd
|
||||
eventType uint32
|
||||
eventData uintptr
|
||||
errno uint32
|
||||
}
|
||||
|
||||
// service provides access to windows service api.
|
||||
type service struct {
|
||||
name string
|
||||
h windows.Handle
|
||||
cWaits *event
|
||||
goWaits *event
|
||||
c chan ctlEvent
|
||||
handler Handler
|
||||
}
|
||||
|
||||
func newService(name string, handler Handler) (*service, error) {
|
||||
var s service
|
||||
var err error
|
||||
s.name = name
|
||||
s.c = make(chan ctlEvent)
|
||||
s.handler = handler
|
||||
s.cWaits, err = newEvent()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.goWaits, err = newEvent()
|
||||
if err != nil {
|
||||
s.cWaits.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func (s *service) close() error {
|
||||
s.cWaits.Close()
|
||||
s.goWaits.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
type exitCode struct {
|
||||
isSvcSpecific bool
|
||||
errno uint32
|
||||
}
|
||||
|
||||
func (s *service) updateStatus(status *Status, ec *exitCode) error {
|
||||
if s.h == 0 {
|
||||
return errors.New("updateStatus with no service status handle")
|
||||
}
|
||||
var t windows.SERVICE_STATUS
|
||||
t.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
|
||||
t.CurrentState = uint32(status.State)
|
||||
if status.Accepts&AcceptStop != 0 {
|
||||
t.ControlsAccepted |= windows.SERVICE_ACCEPT_STOP
|
||||
}
|
||||
if status.Accepts&AcceptShutdown != 0 {
|
||||
t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN
|
||||
}
|
||||
if status.Accepts&AcceptPauseAndContinue != 0 {
|
||||
t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE
|
||||
}
|
||||
if status.Accepts&AcceptParamChange != 0 {
|
||||
t.ControlsAccepted |= windows.SERVICE_ACCEPT_PARAMCHANGE
|
||||
}
|
||||
if status.Accepts&AcceptNetBindChange != 0 {
|
||||
t.ControlsAccepted |= windows.SERVICE_ACCEPT_NETBINDCHANGE
|
||||
}
|
||||
if status.Accepts&AcceptHardwareProfileChange != 0 {
|
||||
t.ControlsAccepted |= windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE
|
||||
}
|
||||
if status.Accepts&AcceptPowerEvent != 0 {
|
||||
t.ControlsAccepted |= windows.SERVICE_ACCEPT_POWEREVENT
|
||||
}
|
||||
if status.Accepts&AcceptSessionChange != 0 {
|
||||
t.ControlsAccepted |= windows.SERVICE_ACCEPT_SESSIONCHANGE
|
||||
}
|
||||
if ec.errno == 0 {
|
||||
t.Win32ExitCode = windows.NO_ERROR
|
||||
t.ServiceSpecificExitCode = windows.NO_ERROR
|
||||
} else if ec.isSvcSpecific {
|
||||
t.Win32ExitCode = uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR)
|
||||
t.ServiceSpecificExitCode = ec.errno
|
||||
} else {
|
||||
t.Win32ExitCode = ec.errno
|
||||
t.ServiceSpecificExitCode = windows.NO_ERROR
|
||||
}
|
||||
t.CheckPoint = status.CheckPoint
|
||||
t.WaitHint = status.WaitHint
|
||||
return windows.SetServiceStatus(s.h, &t)
|
||||
}
|
||||
|
||||
const (
|
||||
sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota
|
||||
sysErrNewThreadInCallback
|
||||
)
|
||||
|
||||
func (s *service) run() {
|
||||
s.goWaits.Wait()
|
||||
s.h = windows.Handle(ssHandle)
|
||||
argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc]
|
||||
args := make([]string, len(argv))
|
||||
for i, a := range argv {
|
||||
args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:])
|
||||
}
|
||||
|
||||
cmdsToHandler := make(chan ChangeRequest)
|
||||
changesFromHandler := make(chan Status)
|
||||
exitFromHandler := make(chan exitCode)
|
||||
|
||||
go func() {
|
||||
ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler)
|
||||
exitFromHandler <- exitCode{ss, errno}
|
||||
}()
|
||||
|
||||
status := Status{State: Stopped}
|
||||
ec := exitCode{isSvcSpecific: true, errno: 0}
|
||||
var outch chan ChangeRequest
|
||||
inch := s.c
|
||||
var cmd Cmd
|
||||
var evtype uint32
|
||||
var evdata uintptr
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case r := <-inch:
|
||||
if r.errno != 0 {
|
||||
ec.errno = r.errno
|
||||
break loop
|
||||
}
|
||||
inch = nil
|
||||
outch = cmdsToHandler
|
||||
cmd = r.cmd
|
||||
evtype = r.eventType
|
||||
evdata = r.eventData
|
||||
case outch <- ChangeRequest{cmd, evtype, evdata, status}:
|
||||
inch = s.c
|
||||
outch = nil
|
||||
case c := <-changesFromHandler:
|
||||
err := s.updateStatus(&c, &ec)
|
||||
if err != nil {
|
||||
// best suitable error number
|
||||
ec.errno = sysErrSetServiceStatusFailed
|
||||
if err2, ok := err.(syscall.Errno); ok {
|
||||
ec.errno = uint32(err2)
|
||||
}
|
||||
break loop
|
||||
}
|
||||
status = c
|
||||
case ec = <-exitFromHandler:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
s.updateStatus(&Status{State: Stopped}, &ec)
|
||||
s.cWaits.Set()
|
||||
}
|
||||
|
||||
func newCallback(fn interface{}) (cb uintptr, err error) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
cb = 0
|
||||
switch v := r.(type) {
|
||||
case string:
|
||||
err = errors.New(v)
|
||||
case error:
|
||||
err = v
|
||||
default:
|
||||
err = errors.New("unexpected panic in syscall.NewCallback")
|
||||
}
|
||||
}()
|
||||
return syscall.NewCallback(fn), nil
|
||||
}
|
||||
|
||||
// BUG(brainman): There is no mechanism to run multiple services
|
||||
// inside one single executable. Perhaps, it can be overcome by
|
||||
// using RegisterServiceCtrlHandlerEx Windows api.
|
||||
|
||||
// Run executes service name by calling appropriate handler function.
|
||||
func Run(name string, handler Handler) error {
|
||||
runtime.LockOSThread()
|
||||
|
||||
tid := windows.GetCurrentThreadId()
|
||||
|
||||
s, err := newService(name, handler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctlHandler := func(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||
e := ctlEvent{cmd: Cmd(ctl), eventType: evtype, eventData: evdata}
|
||||
// We assume that this callback function is running on
|
||||
// the same thread as Run. Nowhere in MS documentation
|
||||
// I could find statement to guarantee that. So putting
|
||||
// check here to verify, otherwise things will go bad
|
||||
// quickly, if ignored.
|
||||
i := windows.GetCurrentThreadId()
|
||||
if i != tid {
|
||||
e.errno = sysErrNewThreadInCallback
|
||||
}
|
||||
s.c <- e
|
||||
// Always return NO_ERROR (0) for now.
|
||||
return 0
|
||||
}
|
||||
|
||||
var svcmain uintptr
|
||||
getServiceMain(&svcmain)
|
||||
t := []windows.SERVICE_TABLE_ENTRY{
|
||||
{syscall.StringToUTF16Ptr(s.name), svcmain},
|
||||
{nil, 0},
|
||||
}
|
||||
|
||||
goWaitsH = uintptr(s.goWaits.h)
|
||||
cWaitsH = uintptr(s.cWaits.h)
|
||||
sName = t[0].ServiceName
|
||||
ctlHandlerExProc, err = newCallback(ctlHandler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go s.run()
|
||||
|
||||
err = windows.StartServiceCtrlDispatcher(&t[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StatusHandle returns service status handle. It is safe to call this function
|
||||
// from inside the Handler.Execute because then it is guaranteed to be set.
|
||||
// This code will have to change once multiple services are possible per process.
|
||||
func StatusHandle() windows.Handle {
|
||||
return windows.Handle(ssHandle)
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
// func servicemain(argc uint32, argv **uint16)
|
||||
TEXT ·servicemain(SB),7,$0
|
||||
MOVL argc+0(FP), AX
|
||||
MOVL AX, ·sArgc(SB)
|
||||
MOVL argv+4(FP), AX
|
||||
MOVL AX, ·sArgv(SB)
|
||||
|
||||
PUSHL BP
|
||||
PUSHL BX
|
||||
PUSHL SI
|
||||
PUSHL DI
|
||||
|
||||
SUBL $12, SP
|
||||
|
||||
MOVL ·sName(SB), AX
|
||||
MOVL AX, (SP)
|
||||
MOVL $·servicectlhandler(SB), AX
|
||||
MOVL AX, 4(SP)
|
||||
MOVL $0, 8(SP)
|
||||
MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX
|
||||
MOVL SP, BP
|
||||
CALL AX
|
||||
MOVL BP, SP
|
||||
CMPL AX, $0
|
||||
JE exit
|
||||
MOVL AX, ·ssHandle(SB)
|
||||
|
||||
MOVL ·goWaitsH(SB), AX
|
||||
MOVL AX, (SP)
|
||||
MOVL ·cSetEvent(SB), AX
|
||||
MOVL SP, BP
|
||||
CALL AX
|
||||
MOVL BP, SP
|
||||
|
||||
MOVL ·cWaitsH(SB), AX
|
||||
MOVL AX, (SP)
|
||||
MOVL $-1, AX
|
||||
MOVL AX, 4(SP)
|
||||
MOVL ·cWaitForSingleObject(SB), AX
|
||||
MOVL SP, BP
|
||||
CALL AX
|
||||
MOVL BP, SP
|
||||
|
||||
exit:
|
||||
ADDL $12, SP
|
||||
|
||||
POPL DI
|
||||
POPL SI
|
||||
POPL BX
|
||||
POPL BP
|
||||
|
||||
MOVL 0(SP), CX
|
||||
ADDL $12, SP
|
||||
JMP CX
|
||||
|
||||
// I do not know why, but this seems to be the only way to call
|
||||
// ctlHandlerProc on Windows 7.
|
||||
|
||||
// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||
TEXT ·servicectlhandler(SB),7,$0
|
||||
MOVL ·ctlHandlerExProc(SB), CX
|
||||
JMP CX
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
// func servicemain(argc uint32, argv **uint16)
|
||||
TEXT ·servicemain(SB),7,$0
|
||||
MOVL CX, ·sArgc(SB)
|
||||
MOVL DX, ·sArgv(SB)
|
||||
|
||||
SUBQ $32, SP // stack for the first 4 syscall params
|
||||
|
||||
MOVQ ·sName(SB), CX
|
||||
MOVQ $·servicectlhandler(SB), DX
|
||||
// BUG(pastarmovj): Figure out a way to pass in context in R8.
|
||||
MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX
|
||||
CALL AX
|
||||
CMPQ AX, $0
|
||||
JE exit
|
||||
MOVQ AX, ·ssHandle(SB)
|
||||
|
||||
MOVQ ·goWaitsH(SB), CX
|
||||
MOVQ ·cSetEvent(SB), AX
|
||||
CALL AX
|
||||
|
||||
MOVQ ·cWaitsH(SB), CX
|
||||
MOVQ $4294967295, DX
|
||||
MOVQ ·cWaitForSingleObject(SB), AX
|
||||
CALL AX
|
||||
|
||||
exit:
|
||||
ADDQ $32, SP
|
||||
RET
|
||||
|
||||
// I do not know why, but this seems to be the only way to call
|
||||
// ctlHandlerProc on Windows 7.
|
||||
|
||||
// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
|
||||
TEXT ·servicectlhandler(SB),7,$0
|
||||
MOVQ ·ctlHandlerExProc(SB), AX
|
||||
JMP AX
|
Loading…
Reference in New Issue