mirror of
https://github.com/containers/podman.git
synced 2025-06-09 00:53:41 +08:00
Vendor in latest Buildah
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
This commit is contained in:
@ -174,7 +174,7 @@ var _ = Describe("Podman images", func() {
|
|||||||
result := podmanTest.Podman([]string{"images", "-q", "-f", "before=foobar.com/before:latest"})
|
result := podmanTest.Podman([]string{"images", "-q", "-f", "before=foobar.com/before:latest"})
|
||||||
result.WaitWithDefaultTimeout()
|
result.WaitWithDefaultTimeout()
|
||||||
Expect(result.ExitCode()).To(Equal(0))
|
Expect(result.ExitCode()).To(Equal(0))
|
||||||
Expect(len(result.OutputToStringArray())).To(Equal(2))
|
Expect(len(result.OutputToStringArray())).To(Equal(1))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman images filter after image", func() {
|
It("podman images filter after image", func() {
|
||||||
@ -191,7 +191,7 @@ var _ = Describe("Podman images", func() {
|
|||||||
result := podmanTest.Podman([]string{"images", "-q", "-f", "after=docker.io/library/alpine:latest"})
|
result := podmanTest.Podman([]string{"images", "-q", "-f", "after=docker.io/library/alpine:latest"})
|
||||||
result.WaitWithDefaultTimeout()
|
result.WaitWithDefaultTimeout()
|
||||||
Expect(result.ExitCode()).To(Equal(0))
|
Expect(result.ExitCode()).To(Equal(0))
|
||||||
Expect(len(result.OutputToStringArray())).To(Equal(1))
|
Expect(len(result.OutputToStringArray())).To(Equal(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman image list filter after image", func() {
|
It("podman image list filter after image", func() {
|
||||||
@ -208,7 +208,7 @@ var _ = Describe("Podman images", func() {
|
|||||||
result := podmanTest.Podman([]string{"image", "list", "-q", "-f", "after=docker.io/library/alpine:latest"})
|
result := podmanTest.Podman([]string{"image", "list", "-q", "-f", "after=docker.io/library/alpine:latest"})
|
||||||
result.WaitWithDefaultTimeout()
|
result.WaitWithDefaultTimeout()
|
||||||
Expect(result.ExitCode()).To(Equal(0))
|
Expect(result.ExitCode()).To(Equal(0))
|
||||||
Expect(len(result.OutputToStringArray())).To(Equal(1))
|
Expect(len(result.OutputToStringArray())).To(Equal(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman images filter dangling", func() {
|
It("podman images filter dangling", func() {
|
||||||
@ -222,7 +222,7 @@ var _ = Describe("Podman images", func() {
|
|||||||
result := podmanTest.Podman([]string{"images", "-q", "-f", "dangling=true"})
|
result := podmanTest.Podman([]string{"images", "-q", "-f", "dangling=true"})
|
||||||
result.WaitWithDefaultTimeout()
|
result.WaitWithDefaultTimeout()
|
||||||
Expect(result.ExitCode()).To(Equal(0))
|
Expect(result.ExitCode()).To(Equal(0))
|
||||||
Expect(len(result.OutputToStringArray())).To(Equal(1))
|
Expect(len(result.OutputToStringArray())).To(Equal(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("podman check for image with sha256: prefix", func() {
|
It("podman check for image with sha256: prefix", func() {
|
||||||
|
@ -30,7 +30,7 @@ github.com/docker/docker 54dddadc7d5d89fe0be88f76979f6f6ab0dede83
|
|||||||
github.com/docker/docker-credential-helpers v0.6.1
|
github.com/docker/docker-credential-helpers v0.6.1
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/docker/go-units v0.3.2
|
github.com/docker/go-units v0.3.2
|
||||||
github.com/docker/libnetwork 5f7a3f68c3d9696229cdc09b8cb3d84c06b13e4e
|
github.com/docker/libnetwork 1a06131fb8a047d919f7deaf02a4c414d7884b83
|
||||||
github.com/docker/libtrust aabc10ec26b754e797f9028f4589c5b7bd90dc20
|
github.com/docker/libtrust aabc10ec26b754e797f9028f4589c5b7bd90dc20
|
||||||
github.com/docker/spdystream 6480d4af844c189cf5dd913db24ddd339d3a4f85
|
github.com/docker/spdystream 6480d4af844c189cf5dd913db24ddd339d3a4f85
|
||||||
github.com/fatih/camelcase v1.0.0
|
github.com/fatih/camelcase v1.0.0
|
||||||
@ -93,7 +93,7 @@ k8s.io/apimachinery kubernetes-1.10.13-beta.0 https://github.com/kubernetes/apim
|
|||||||
k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go
|
k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go
|
||||||
github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7
|
github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7
|
||||||
github.com/varlink/go 3ac79db6fd6aec70924193b090962f92985fe199
|
github.com/varlink/go 3ac79db6fd6aec70924193b090962f92985fe199
|
||||||
github.com/containers/buildah v1.7.2
|
github.com/containers/buildah fcc12bdadf6a5fab77e62e1bd12663bb6fbc3eda
|
||||||
# TODO: Gotty has not been updated since 2012. Can we find replacement?
|
# TODO: Gotty has not been updated since 2012. Can we find replacement?
|
||||||
github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512
|
github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512
|
||||||
github.com/fsouza/go-dockerclient v1.3.0
|
github.com/fsouza/go-dockerclient v1.3.0
|
||||||
@ -112,3 +112,4 @@ gopkg.in/tomb.v1 v1
|
|||||||
github.com/spf13/cobra v0.0.3
|
github.com/spf13/cobra v0.0.3
|
||||||
github.com/inconshreveable/mousetrap v1.0.0
|
github.com/inconshreveable/mousetrap v1.0.0
|
||||||
gopkg.in/fsnotify.v1 v1.4.7
|
gopkg.in/fsnotify.v1 v1.4.7
|
||||||
|
github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb
|
||||||
|
45
vendor/github.com/containers/buildah/add.go
generated
vendored
45
vendor/github.com/containers/buildah/add.go
generated
vendored
@ -209,6 +209,10 @@ func DockerIgnoreHelper(lines []string, contextDir string) []DockerIgnore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func addHelper(excludes []DockerIgnore, extract bool, dest string, destfi os.FileInfo, hostOwner idtools.IDPair, options AddAndCopyOptions, copyFileWithTar, copyWithTar, untarPath func(src, dest string) error, source ...string) error {
|
func addHelper(excludes []DockerIgnore, extract bool, dest string, destfi os.FileInfo, hostOwner idtools.IDPair, options AddAndCopyOptions, copyFileWithTar, copyWithTar, untarPath func(src, dest string) error, source ...string) error {
|
||||||
|
dirsInDockerignore, err := getDirsInDockerignore(options.ContextDir, excludes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error checking directories in .dockerignore")
|
||||||
|
}
|
||||||
for _, src := range source {
|
for _, src := range source {
|
||||||
if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
|
if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
|
||||||
// We assume that source is a file, and we're copying
|
// We assume that source is a file, and we're copying
|
||||||
@ -274,10 +278,15 @@ func addHelper(excludes []DockerIgnore, extract bool, dest string, destfi os.Fil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !match {
|
prefix, exist := dirsInDockerignore[exclude.ExcludePath]
|
||||||
|
hasPrefix := false
|
||||||
|
if exist {
|
||||||
|
hasPrefix = filepath.HasPrefix(path, prefix)
|
||||||
|
}
|
||||||
|
if !(match || hasPrefix) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if exclude.IsExcluded {
|
if (hasPrefix && exclude.IsExcluded) || (match && exclude.IsExcluded) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@ -333,3 +342,35 @@ func addHelper(excludes []DockerIgnore, extract bool, dest string, destfi os.Fil
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDirsInDockerignore(srcAbsPath string, excludes []DockerIgnore) (map[string]string, error) {
|
||||||
|
visitedDir := make(map[string]string)
|
||||||
|
if len(excludes) == 0 {
|
||||||
|
return visitedDir, nil
|
||||||
|
}
|
||||||
|
err := filepath.Walk(srcAbsPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
for _, exclude := range excludes {
|
||||||
|
match, err := filepath.Match(filepath.Clean(exclude.ExcludePath), filepath.Clean(path))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !match {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, exist := visitedDir[exclude.ExcludePath]; exist {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
visitedDir[exclude.ExcludePath] = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return visitedDir, err
|
||||||
|
}
|
||||||
|
return visitedDir, nil
|
||||||
|
}
|
||||||
|
8
vendor/github.com/containers/buildah/buildah.go
generated
vendored
8
vendor/github.com/containers/buildah/buildah.go
generated
vendored
@ -26,7 +26,7 @@ const (
|
|||||||
Package = "buildah"
|
Package = "buildah"
|
||||||
// Version for the Package. Bump version in contrib/rpm/buildah.spec
|
// Version for the Package. Bump version in contrib/rpm/buildah.spec
|
||||||
// too.
|
// too.
|
||||||
Version = "1.7.2"
|
Version = "1.8-dev"
|
||||||
// The value we use to identify what type of information, currently a
|
// The value we use to identify what type of information, currently a
|
||||||
// serialized Builder structure, we are using as per-container state.
|
// serialized Builder structure, we are using as per-container state.
|
||||||
// This should only be changed when we make incompatible changes to
|
// This should only be changed when we make incompatible changes to
|
||||||
@ -284,6 +284,12 @@ type CommonBuildOptions struct {
|
|||||||
CPUSetMems string
|
CPUSetMems string
|
||||||
// Memory is the upper limit (in bytes) on how much memory running containers can use.
|
// Memory is the upper limit (in bytes) on how much memory running containers can use.
|
||||||
Memory int64
|
Memory int64
|
||||||
|
// DNSSearch is the list of DNS search domains to add to the build container's /etc/resolv.conf
|
||||||
|
DNSSearch []string
|
||||||
|
// DNSServers is the list of DNS servers to add to the build container's /etc/resolv.conf
|
||||||
|
DNSServers []string
|
||||||
|
// DNSOptions is the list of DNS
|
||||||
|
DNSOptions []string
|
||||||
// MemorySwap limits the amount of memory and swap together.
|
// MemorySwap limits the amount of memory and swap together.
|
||||||
MemorySwap int64
|
MemorySwap int64
|
||||||
// LabelOpts is the a slice of fields of an SELinux context, given in "field:pair" format, or "disable".
|
// LabelOpts is the a slice of fields of an SELinux context, given in "field:pair" format, or "disable".
|
||||||
|
2
vendor/github.com/containers/buildah/chroot/run.go
generated
vendored
2
vendor/github.com/containers/buildah/chroot/run.go
generated
vendored
@ -18,7 +18,7 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/containers/buildah/bind"
|
"github.com/containers/buildah/bind"
|
||||||
"github.com/containers/buildah/unshare"
|
"github.com/containers/buildah/pkg/unshare"
|
||||||
"github.com/containers/buildah/util"
|
"github.com/containers/buildah/util"
|
||||||
"github.com/containers/storage/pkg/ioutils"
|
"github.com/containers/storage/pkg/ioutils"
|
||||||
"github.com/containers/storage/pkg/mount"
|
"github.com/containers/storage/pkg/mount"
|
||||||
|
46
vendor/github.com/containers/buildah/commit.go
generated
vendored
46
vendor/github.com/containers/buildah/commit.go
generated
vendored
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/buildah/pkg/blobcache"
|
"github.com/containers/buildah/pkg/blobcache"
|
||||||
@ -18,6 +19,7 @@ import (
|
|||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
"github.com/containers/storage/pkg/stringid"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -110,10 +112,28 @@ type PushOptions struct {
|
|||||||
// Commit writes the contents of the container, along with its updated
|
// Commit writes the contents of the container, along with its updated
|
||||||
// configuration, to a new image in the specified location, and if we know how,
|
// configuration, to a new image in the specified location, and if we know how,
|
||||||
// add any additional tags that were specified. Returns the ID of the new image
|
// add any additional tags that were specified. Returns the ID of the new image
|
||||||
// if commit was successful and the image destination was local
|
// if commit was successful and the image destination was local.
|
||||||
func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options CommitOptions) (string, reference.Canonical, digest.Digest, error) {
|
func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options CommitOptions) (string, reference.Canonical, digest.Digest, error) {
|
||||||
var imgID string
|
var imgID string
|
||||||
|
|
||||||
|
// If we weren't given a name, build a destination reference using a
|
||||||
|
// temporary name that we'll remove later. The correct thing to do
|
||||||
|
// would be to read the manifest and configuration blob, and ask the
|
||||||
|
// manifest for the ID that we'd give the image, but that computation
|
||||||
|
// requires that we know the digests of the layer blobs, which we don't
|
||||||
|
// want to compute here because we'll have to do it again when
|
||||||
|
// cp.Image() instantiates a source image, and we don't want to do the
|
||||||
|
// work twice.
|
||||||
|
nameToRemove := ""
|
||||||
|
if dest == nil {
|
||||||
|
nameToRemove = stringid.GenerateRandomID() + "-tmp"
|
||||||
|
dest2, err := is.Transport.ParseStoreReference(b.store, nameToRemove)
|
||||||
|
if err != nil {
|
||||||
|
return imgID, nil, "", errors.Wrapf(err, "error creating temporary destination reference for image")
|
||||||
|
}
|
||||||
|
dest = dest2
|
||||||
|
}
|
||||||
|
|
||||||
systemContext := getSystemContext(b.store, options.SystemContext, options.SignaturePolicyPath)
|
systemContext := getSystemContext(b.store, options.SystemContext, options.SignaturePolicyPath)
|
||||||
|
|
||||||
blocked, err := isReferenceBlocked(dest, systemContext)
|
blocked, err := isReferenceBlocked(dest, systemContext)
|
||||||
@ -148,10 +168,13 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Build an image reference from which we can copy the finished image.
|
||||||
src, err := b.makeImageRef(options.PreferredManifestType, options.Parent, exportBaseLayers, options.Squash, options.BlobDirectory, options.Compression, options.HistoryTimestamp, options.OmitTimestamp)
|
src, err := b.makeImageRef(options.PreferredManifestType, options.Parent, exportBaseLayers, options.Squash, options.BlobDirectory, options.Compression, options.HistoryTimestamp, options.OmitTimestamp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return imgID, nil, "", errors.Wrapf(err, "error computing layer digests and building metadata for container %q", b.ContainerID)
|
return imgID, nil, "", errors.Wrapf(err, "error computing layer digests and building metadata for container %q", b.ContainerID)
|
||||||
}
|
}
|
||||||
|
// In case we're using caching, decide how to handle compression for a cache.
|
||||||
|
// If we're using blob caching, set it up for the source.
|
||||||
var maybeCachedSrc = types.ImageReference(src)
|
var maybeCachedSrc = types.ImageReference(src)
|
||||||
var maybeCachedDest = types.ImageReference(dest)
|
var maybeCachedDest = types.ImageReference(dest)
|
||||||
if options.BlobDirectory != "" {
|
if options.BlobDirectory != "" {
|
||||||
@ -181,6 +204,8 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options
|
|||||||
if manifestBytes, err = cp.Image(ctx, policyContext, maybeCachedDest, maybeCachedSrc, getCopyOptions(b.store, options.ReportWriter, maybeCachedSrc, nil, maybeCachedDest, systemContext, "")); err != nil {
|
if manifestBytes, err = cp.Image(ctx, policyContext, maybeCachedDest, maybeCachedSrc, getCopyOptions(b.store, options.ReportWriter, maybeCachedSrc, nil, maybeCachedDest, systemContext, "")); err != nil {
|
||||||
return imgID, nil, "", errors.Wrapf(err, "error copying layers and metadata for container %q", b.ContainerID)
|
return imgID, nil, "", errors.Wrapf(err, "error copying layers and metadata for container %q", b.ContainerID)
|
||||||
}
|
}
|
||||||
|
// If we've got more names to attach, and we know how to do that for
|
||||||
|
// the transport that we're writing the new image to, add them now.
|
||||||
if len(options.AdditionalTags) > 0 {
|
if len(options.AdditionalTags) > 0 {
|
||||||
switch dest.Transport().Name() {
|
switch dest.Transport().Name() {
|
||||||
case is.Transport.Name():
|
case is.Transport.Name():
|
||||||
@ -201,10 +226,25 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options
|
|||||||
if err != nil && err != storage.ErrImageUnknown {
|
if err != nil && err != storage.ErrImageUnknown {
|
||||||
return imgID, nil, "", errors.Wrapf(err, "error locating image %q in local storage", transports.ImageName(dest))
|
return imgID, nil, "", errors.Wrapf(err, "error locating image %q in local storage", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
imgID = img.ID
|
imgID = img.ID
|
||||||
|
prunedNames := make([]string, 0, len(img.Names))
|
||||||
|
for _, name := range img.Names {
|
||||||
|
if !(nameToRemove != "" && strings.Contains(name, nameToRemove)) {
|
||||||
|
prunedNames = append(prunedNames, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(prunedNames) < len(img.Names) {
|
||||||
|
if err = b.store.SetNames(imgID, prunedNames); err != nil {
|
||||||
|
return imgID, nil, "", errors.Wrapf(err, "failed to prune temporary name from image %q", imgID)
|
||||||
|
}
|
||||||
|
logrus.Debugf("reassigned names %v to image %q", prunedNames, img.ID)
|
||||||
|
dest2, err := is.Transport.ParseStoreReference(b.store, "@"+imgID)
|
||||||
|
if err != nil {
|
||||||
|
return imgID, nil, "", errors.Wrapf(err, "error creating unnamed destination reference for image")
|
||||||
|
}
|
||||||
|
dest = dest2
|
||||||
|
}
|
||||||
if options.IIDFile != "" {
|
if options.IIDFile != "" {
|
||||||
if err = ioutil.WriteFile(options.IIDFile, []byte(img.ID), 0644); err != nil {
|
if err = ioutil.WriteFile(options.IIDFile, []byte(img.ID), 0644); err != nil {
|
||||||
return imgID, nil, "", errors.Wrapf(err, "failed to write image ID to file %q", options.IIDFile)
|
return imgID, nil, "", errors.Wrapf(err, "failed to write image ID to file %q", options.IIDFile)
|
||||||
|
2
vendor/github.com/containers/buildah/common.go
generated
vendored
2
vendor/github.com/containers/buildah/common.go
generated
vendored
@ -5,7 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/containers/buildah/unshare"
|
"github.com/containers/buildah/pkg/unshare"
|
||||||
cp "github.com/containers/image/copy"
|
cp "github.com/containers/image/copy"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
|
10
vendor/github.com/containers/buildah/image.go
generated
vendored
10
vendor/github.com/containers/buildah/image.go
generated
vendored
@ -9,6 +9,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/buildah/docker"
|
"github.com/containers/buildah/docker"
|
||||||
@ -661,6 +662,13 @@ func (b *Builder) makeImageRef(manifestType, parent string, exporting bool, squa
|
|||||||
if historyTimestamp != nil {
|
if historyTimestamp != nil {
|
||||||
created = historyTimestamp.UTC()
|
created = historyTimestamp.UTC()
|
||||||
}
|
}
|
||||||
|
createdBy := b.CreatedBy()
|
||||||
|
if createdBy == "" {
|
||||||
|
createdBy = strings.Join(b.Shell(), " ")
|
||||||
|
if createdBy == "" {
|
||||||
|
createdBy = "/bin/sh"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if omitTimestamp {
|
if omitTimestamp {
|
||||||
created = time.Unix(0, 0)
|
created = time.Unix(0, 0)
|
||||||
@ -677,7 +685,7 @@ func (b *Builder) makeImageRef(manifestType, parent string, exporting bool, squa
|
|||||||
oconfig: oconfig,
|
oconfig: oconfig,
|
||||||
dconfig: dconfig,
|
dconfig: dconfig,
|
||||||
created: created,
|
created: created,
|
||||||
createdBy: b.CreatedBy(),
|
createdBy: createdBy,
|
||||||
historyComment: b.HistoryComment(),
|
historyComment: b.HistoryComment(),
|
||||||
annotations: b.Annotations(),
|
annotations: b.Annotations(),
|
||||||
preferredManifestType: manifestType,
|
preferredManifestType: manifestType,
|
||||||
|
653
vendor/github.com/containers/buildah/imagebuildah/build.go
generated
vendored
653
vendor/github.com/containers/buildah/imagebuildah/build.go
generated
vendored
@ -10,7 +10,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -28,7 +27,6 @@ import (
|
|||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
"github.com/containers/storage/pkg/stringid"
|
|
||||||
docker "github.com/fsouza/go-dockerclient"
|
docker "github.com/fsouza/go-dockerclient"
|
||||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
@ -215,7 +213,8 @@ type Executor struct {
|
|||||||
useCache bool
|
useCache bool
|
||||||
removeIntermediateCtrs bool
|
removeIntermediateCtrs bool
|
||||||
forceRmIntermediateCtrs bool
|
forceRmIntermediateCtrs bool
|
||||||
imageMap map[string]string // Used to map images that we create to handle the AS construct.
|
imageMap map[string]string // Used to map images that we create to handle the AS construct.
|
||||||
|
containerMap map[string]*buildah.Builder // Used to map from image names to only-created-for-the-rootfs containers.
|
||||||
blobDirectory string
|
blobDirectory string
|
||||||
excludes []string
|
excludes []string
|
||||||
unusedArgs map[string]struct{}
|
unusedArgs map[string]struct{}
|
||||||
@ -496,6 +495,8 @@ func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) err
|
|||||||
} else if len(copy.From) > 0 {
|
} else if len(copy.From) > 0 {
|
||||||
if other, ok := s.executor.stages[copy.From]; ok && other.index < s.index {
|
if other, ok := s.executor.stages[copy.From]; ok && other.index < s.index {
|
||||||
sources = append(sources, filepath.Join(other.mountPoint, src))
|
sources = append(sources, filepath.Join(other.mountPoint, src))
|
||||||
|
} else if builder, ok := s.executor.containerMap[copy.From]; ok {
|
||||||
|
sources = append(sources, filepath.Join(builder.MountPoint, src))
|
||||||
} else {
|
} else {
|
||||||
return errors.Errorf("the stage %q has not been built", copy.From)
|
return errors.Errorf("the stage %q has not been built", copy.From)
|
||||||
}
|
}
|
||||||
@ -654,6 +655,7 @@ func NewExecutor(store storage.Store, options BuildOptions) (*Executor, error) {
|
|||||||
removeIntermediateCtrs: options.RemoveIntermediateCtrs,
|
removeIntermediateCtrs: options.RemoveIntermediateCtrs,
|
||||||
forceRmIntermediateCtrs: options.ForceRmIntermediateCtrs,
|
forceRmIntermediateCtrs: options.ForceRmIntermediateCtrs,
|
||||||
imageMap: make(map[string]string),
|
imageMap: make(map[string]string),
|
||||||
|
containerMap: make(map[string]*buildah.Builder),
|
||||||
blobDirectory: options.BlobDirectory,
|
blobDirectory: options.BlobDirectory,
|
||||||
unusedArgs: make(map[string]struct{}),
|
unusedArgs: make(map[string]struct{}),
|
||||||
}
|
}
|
||||||
@ -680,18 +682,18 @@ func NewExecutor(store storage.Store, options BuildOptions) (*Executor, error) {
|
|||||||
return &exec, nil
|
return &exec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare creates a working container based on the specified image, or if one
|
// prepare creates a working container based on the specified image, or if one
|
||||||
// isn't specified, the first argument passed to the first FROM instruction we
|
// isn't specified, the first argument passed to the first FROM instruction we
|
||||||
// can find in the stage's parsed tree.
|
// can find in the stage's parsed tree.
|
||||||
func (s *StageExecutor) Prepare(ctx context.Context, stage imagebuilder.Stage, from string) error {
|
func (s *StageExecutor) prepare(ctx context.Context, stage imagebuilder.Stage, from string, initializeIBConfig, rebase bool) (builder *buildah.Builder, err error) {
|
||||||
ib := stage.Builder
|
ib := stage.Builder
|
||||||
node := stage.Node
|
node := stage.Node
|
||||||
|
|
||||||
if from == "" {
|
if from == "" {
|
||||||
base, err := ib.From(node)
|
base, err := ib.From(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Prepare(node.Children=%#v)", node.Children)
|
logrus.Debugf("prepare(node.Children=%#v)", node.Children)
|
||||||
return errors.Wrapf(err, "error determining starting point for build")
|
return nil, errors.Wrapf(err, "error determining starting point for build")
|
||||||
}
|
}
|
||||||
from = base
|
from = base
|
||||||
}
|
}
|
||||||
@ -707,9 +709,11 @@ func (s *StageExecutor) Prepare(ctx context.Context, stage imagebuilder.Stage, f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("FROM %#v", displayFrom)
|
if initializeIBConfig && rebase {
|
||||||
if !s.executor.quiet {
|
logrus.Debugf("FROM %#v", displayFrom)
|
||||||
s.executor.log("FROM %s", displayFrom)
|
if !s.executor.quiet {
|
||||||
|
s.executor.log("FROM %s", displayFrom)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builderOptions := buildah.BuilderOptions{
|
builderOptions := buildah.BuilderOptions{
|
||||||
@ -737,74 +741,79 @@ func (s *StageExecutor) Prepare(ctx context.Context, stage imagebuilder.Stage, f
|
|||||||
if asImageFound, ok := s.executor.imageMap[from]; ok {
|
if asImageFound, ok := s.executor.imageMap[from]; ok {
|
||||||
builderOptions.FromImage = asImageFound
|
builderOptions.FromImage = asImageFound
|
||||||
}
|
}
|
||||||
builder, err := buildah.NewBuilder(ctx, s.executor.store, builderOptions)
|
builder, err = buildah.NewBuilder(ctx, s.executor.store, builderOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error creating build container")
|
return nil, errors.Wrapf(err, "error creating build container")
|
||||||
}
|
}
|
||||||
|
|
||||||
volumes := map[string]struct{}{}
|
if initializeIBConfig {
|
||||||
for _, v := range builder.Volumes() {
|
volumes := map[string]struct{}{}
|
||||||
volumes[v] = struct{}{}
|
for _, v := range builder.Volumes() {
|
||||||
}
|
volumes[v] = struct{}{}
|
||||||
ports := map[docker.Port]struct{}{}
|
|
||||||
for _, p := range builder.Ports() {
|
|
||||||
ports[docker.Port(p)] = struct{}{}
|
|
||||||
}
|
|
||||||
dConfig := docker.Config{
|
|
||||||
Hostname: builder.Hostname(),
|
|
||||||
Domainname: builder.Domainname(),
|
|
||||||
User: builder.User(),
|
|
||||||
Env: builder.Env(),
|
|
||||||
Cmd: builder.Cmd(),
|
|
||||||
Image: from,
|
|
||||||
Volumes: volumes,
|
|
||||||
WorkingDir: builder.WorkDir(),
|
|
||||||
Entrypoint: builder.Entrypoint(),
|
|
||||||
Labels: builder.Labels(),
|
|
||||||
Shell: builder.Shell(),
|
|
||||||
StopSignal: builder.StopSignal(),
|
|
||||||
OnBuild: builder.OnBuild(),
|
|
||||||
ExposedPorts: ports,
|
|
||||||
}
|
|
||||||
var rootfs *docker.RootFS
|
|
||||||
if builder.Docker.RootFS != nil {
|
|
||||||
rootfs = &docker.RootFS{
|
|
||||||
Type: builder.Docker.RootFS.Type,
|
|
||||||
}
|
}
|
||||||
for _, id := range builder.Docker.RootFS.DiffIDs {
|
ports := map[docker.Port]struct{}{}
|
||||||
rootfs.Layers = append(rootfs.Layers, id.String())
|
for _, p := range builder.Ports() {
|
||||||
|
ports[docker.Port(p)] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
dConfig := docker.Config{
|
||||||
dImage := docker.Image{
|
Hostname: builder.Hostname(),
|
||||||
Parent: builder.FromImage,
|
Domainname: builder.Domainname(),
|
||||||
ContainerConfig: dConfig,
|
User: builder.User(),
|
||||||
Container: builder.Container,
|
Env: builder.Env(),
|
||||||
Author: builder.Maintainer(),
|
Cmd: builder.Cmd(),
|
||||||
Architecture: builder.Architecture(),
|
Image: from,
|
||||||
RootFS: rootfs,
|
Volumes: volumes,
|
||||||
}
|
WorkingDir: builder.WorkDir(),
|
||||||
dImage.Config = &dImage.ContainerConfig
|
Entrypoint: builder.Entrypoint(),
|
||||||
err = ib.FromImage(&dImage, node)
|
Labels: builder.Labels(),
|
||||||
if err != nil {
|
Shell: builder.Shell(),
|
||||||
if err2 := builder.Delete(); err2 != nil {
|
StopSignal: builder.StopSignal(),
|
||||||
logrus.Debugf("error deleting container which we failed to update: %v", err2)
|
OnBuild: builder.OnBuild(),
|
||||||
|
ExposedPorts: ports,
|
||||||
|
}
|
||||||
|
var rootfs *docker.RootFS
|
||||||
|
if builder.Docker.RootFS != nil {
|
||||||
|
rootfs = &docker.RootFS{
|
||||||
|
Type: builder.Docker.RootFS.Type,
|
||||||
|
}
|
||||||
|
for _, id := range builder.Docker.RootFS.DiffIDs {
|
||||||
|
rootfs.Layers = append(rootfs.Layers, id.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dImage := docker.Image{
|
||||||
|
Parent: builder.FromImage,
|
||||||
|
ContainerConfig: dConfig,
|
||||||
|
Container: builder.Container,
|
||||||
|
Author: builder.Maintainer(),
|
||||||
|
Architecture: builder.Architecture(),
|
||||||
|
RootFS: rootfs,
|
||||||
|
}
|
||||||
|
dImage.Config = &dImage.ContainerConfig
|
||||||
|
err = ib.FromImage(&dImage, node)
|
||||||
|
if err != nil {
|
||||||
|
if err2 := builder.Delete(); err2 != nil {
|
||||||
|
logrus.Debugf("error deleting container which we failed to update: %v", err2)
|
||||||
|
}
|
||||||
|
return nil, errors.Wrapf(err, "error updating build context")
|
||||||
}
|
}
|
||||||
return errors.Wrapf(err, "error updating build context")
|
|
||||||
}
|
}
|
||||||
mountPoint, err := builder.Mount(builder.MountLabel)
|
mountPoint, err := builder.Mount(builder.MountLabel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err2 := builder.Delete(); err2 != nil {
|
if err2 := builder.Delete(); err2 != nil {
|
||||||
logrus.Debugf("error deleting container which we failed to mount: %v", err2)
|
logrus.Debugf("error deleting container which we failed to mount: %v", err2)
|
||||||
}
|
}
|
||||||
return errors.Wrapf(err, "error mounting new container")
|
return nil, errors.Wrapf(err, "error mounting new container")
|
||||||
|
}
|
||||||
|
if rebase {
|
||||||
|
// Make this our "current" working container.
|
||||||
|
s.mountPoint = mountPoint
|
||||||
|
s.builder = builder
|
||||||
|
// Add the top layer of this image to b.topLayers so we can
|
||||||
|
// keep track of them when building with cached images.
|
||||||
|
s.executor.topLayers = append(s.executor.topLayers, builder.TopLayer)
|
||||||
}
|
}
|
||||||
s.mountPoint = mountPoint
|
|
||||||
s.builder = builder
|
|
||||||
// Add the top layer of this image to b.topLayers so we can keep track of them
|
|
||||||
// when building with cached images.
|
|
||||||
s.executor.topLayers = append(s.executor.topLayers, builder.TopLayer)
|
|
||||||
logrus.Debugln("Container ID:", builder.ContainerID)
|
logrus.Debugln("Container ID:", builder.ContainerID)
|
||||||
return nil
|
return builder, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes the stage's working container, if we have one.
|
// Delete deletes the stage's working container, if we have one.
|
||||||
@ -816,47 +825,118 @@ func (s *StageExecutor) Delete() (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveNameToImageRef creates a types.ImageReference from b.output
|
// resolveNameToImageRef creates a types.ImageReference for the output name in local storage
|
||||||
func (b *Executor) resolveNameToImageRef(output string) (types.ImageReference, error) {
|
func (b *Executor) resolveNameToImageRef(output string) (types.ImageReference, error) {
|
||||||
var (
|
imageRef, err := alltransports.ParseImageName(output)
|
||||||
imageRef types.ImageReference
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if output != "" {
|
|
||||||
imageRef, err = alltransports.ParseImageName(output)
|
|
||||||
if err != nil {
|
|
||||||
candidates, _, _, err := util.ResolveName(output, "", b.systemContext, b.store)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "error parsing target image name %q", output)
|
|
||||||
}
|
|
||||||
if len(candidates) == 0 {
|
|
||||||
return nil, errors.Errorf("error parsing target image name %q", output)
|
|
||||||
}
|
|
||||||
imageRef2, err2 := is.Transport.ParseStoreReference(b.store, candidates[0])
|
|
||||||
if err2 != nil {
|
|
||||||
return nil, errors.Wrapf(err, "error parsing target image name %q", output)
|
|
||||||
}
|
|
||||||
return imageRef2, nil
|
|
||||||
}
|
|
||||||
return imageRef, nil
|
|
||||||
}
|
|
||||||
imageRef, err = is.Transport.ParseStoreReference(b.store, "@"+stringid.GenerateRandomID())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error parsing reference for image to be written")
|
candidates, _, _, err := util.ResolveName(output, "", b.systemContext, b.store)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error parsing target image name %q", output)
|
||||||
|
}
|
||||||
|
if len(candidates) == 0 {
|
||||||
|
return nil, errors.Errorf("error parsing target image name %q", output)
|
||||||
|
}
|
||||||
|
imageRef2, err2 := is.Transport.ParseStoreReference(b.store, candidates[0])
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error parsing target image name %q", output)
|
||||||
|
}
|
||||||
|
return imageRef2, nil
|
||||||
}
|
}
|
||||||
return imageRef, nil
|
return imageRef, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute runs each of the steps in the stage's parsed tree, in turn.
|
// stepRequiresCommit indicates whether or not the step should be followed by
|
||||||
func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage) (imgID string, ref reference.Canonical, err error) {
|
// committing the in-progress container to create an intermediate image.
|
||||||
ib := stage.Builder
|
func (*StageExecutor) stepRequiresCommit(step *imagebuilder.Step) bool {
|
||||||
node := stage.Node
|
switch strings.ToUpper(step.Command) {
|
||||||
checkForLayers := true
|
case "ADD", "COPY", "RUN":
|
||||||
children := node.Children
|
return true
|
||||||
commitName := s.output
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
for i, node := range node.Children {
|
// getImageRootfs checks for an image matching the passed-in name in local
|
||||||
// Resolve any arguments in this instruction so that we don't have to.
|
// storage. If it isn't found, it pulls down a copy. Then, if we don't have a
|
||||||
|
// working container root filesystem based on the image, it creates one. Then
|
||||||
|
// it returns that root filesystem's location.
|
||||||
|
func (s *StageExecutor) getImageRootfs(ctx context.Context, stage imagebuilder.Stage, image string) (mountPoint string, err error) {
|
||||||
|
if builder, ok := s.executor.containerMap[image]; ok {
|
||||||
|
return builder.MountPoint, nil
|
||||||
|
}
|
||||||
|
builder, err := s.prepare(ctx, stage, image, false, false)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
s.executor.containerMap[image] = builder
|
||||||
|
return builder.MountPoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute runs each of the steps in the stage's parsed tree, in turn.
|
||||||
|
func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, base string) (imgID string, ref reference.Canonical, err error) {
|
||||||
|
ib := stage.Builder
|
||||||
|
checkForLayers := s.executor.layers && s.executor.useCache
|
||||||
|
|
||||||
|
// If the base image's name corresponds to the result of an earlier
|
||||||
|
// stage, substitute that image's ID for the base image's name here.
|
||||||
|
// If not, then go on assuming that it's just a regular image that's
|
||||||
|
// either in local storage, or one that we have to pull from a
|
||||||
|
// registry.
|
||||||
|
if stageImage, isPreviousStage := s.executor.imageMap[base]; isPreviousStage {
|
||||||
|
base = stageImage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the (first) working container for this stage. Reinitializing
|
||||||
|
// the imagebuilder configuration may alter the list of steps we have,
|
||||||
|
// so take a snapshot of them *after* that.
|
||||||
|
if _, err := s.prepare(ctx, stage, base, true, true); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
children := stage.Node.Children
|
||||||
|
|
||||||
|
// A helper function to only log "COMMIT" as an explicit step if it's
|
||||||
|
// the very last step of a (possibly multi-stage) build.
|
||||||
|
logCommit := func(output string, instruction int) {
|
||||||
|
if instruction < len(children)-1 || s.index < s.stages-1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
commitMessage := "COMMIT"
|
||||||
|
if output != "" {
|
||||||
|
commitMessage = fmt.Sprintf("%s %s", commitMessage, output)
|
||||||
|
}
|
||||||
|
logrus.Debugf(commitMessage)
|
||||||
|
if !s.executor.quiet {
|
||||||
|
s.executor.log(commitMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logImageID := func(imgID string) {
|
||||||
|
if s.executor.iidfile == "" {
|
||||||
|
fmt.Fprintf(s.executor.out, "--> %s\n", imgID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(children) == 0 {
|
||||||
|
// There are no steps.
|
||||||
|
if s.builder.FromImageID == "" || s.executor.squash {
|
||||||
|
// We either don't have a base image, or we need to
|
||||||
|
// squash the contents of the base image. Whichever is
|
||||||
|
// the case, we need to commit() to create a new image.
|
||||||
|
logCommit(s.output, -1)
|
||||||
|
if imgID, ref, err = s.commit(ctx, ib, getCreatedBy(nil), s.output); err != nil {
|
||||||
|
return "", nil, errors.Wrapf(err, "error committing base container")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We don't need to squash the base image, so just
|
||||||
|
// reuse the base image.
|
||||||
|
logCommit(s.output, -1)
|
||||||
|
if imgID, ref, err = s.copyExistingImage(ctx, s.builder.FromImageID, s.output); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logImageID(imgID)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, node := range children {
|
||||||
|
// Resolve any arguments in this instruction.
|
||||||
step := ib.Step()
|
step := ib.Step()
|
||||||
if err := step.Resolve(node); err != nil {
|
if err := step.Resolve(node); err != nil {
|
||||||
return "", nil, errors.Wrapf(err, "error resolving step %+v", *node)
|
return "", nil, errors.Wrapf(err, "error resolving step %+v", *node)
|
||||||
@ -868,7 +948,7 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage) (
|
|||||||
|
|
||||||
// If this instruction declares an argument, remove it from the
|
// If this instruction declares an argument, remove it from the
|
||||||
// set of arguments that we were passed but which we haven't
|
// set of arguments that we were passed but which we haven't
|
||||||
// seen used by the Dockerfile.
|
// yet seen used by the Dockerfile.
|
||||||
if step.Command == "arg" {
|
if step.Command == "arg" {
|
||||||
for _, Arg := range step.Args {
|
for _, Arg := range step.Args {
|
||||||
list := strings.SplitN(Arg, "=", 2)
|
list := strings.SplitN(Arg, "=", 2)
|
||||||
@ -884,12 +964,17 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage) (
|
|||||||
s.copyFrom = s.executor.contextDir
|
s.copyFrom = s.executor.contextDir
|
||||||
for _, n := range step.Flags {
|
for _, n := range step.Flags {
|
||||||
if strings.Contains(n, "--from") && (step.Command == "copy" || step.Command == "add") {
|
if strings.Contains(n, "--from") && (step.Command == "copy" || step.Command == "add") {
|
||||||
|
var mountPoint string
|
||||||
arr := strings.Split(n, "=")
|
arr := strings.Split(n, "=")
|
||||||
stage, ok := s.executor.stages[arr[1]]
|
otherStage, ok := s.executor.stages[arr[1]]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", nil, errors.Errorf("%s --from=%s: no stage found with that name", step.Command, arr[1])
|
if mountPoint, err = s.getImageRootfs(ctx, stage, arr[1]); err != nil {
|
||||||
|
return "", nil, errors.Errorf("%s --from=%s: no stage or image found with that name", step.Command, arr[1])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mountPoint = otherStage.mountPoint
|
||||||
}
|
}
|
||||||
s.copyFrom = stage.mountPoint
|
s.copyFrom = mountPoint
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -903,101 +988,159 @@ func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage) (
|
|||||||
noRunsRemaining = !ib.RequiresStart(&parser.Node{Children: children[i+1:]})
|
noRunsRemaining = !ib.RequiresStart(&parser.Node{Children: children[i+1:]})
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're doing a single-layer build and not looking to take
|
// If we're doing a single-layer build, just process the
|
||||||
// shortcuts using the cache, make a note of the instruction,
|
// instruction.
|
||||||
// process it, and then move on to the next instruction.
|
if !s.executor.layers {
|
||||||
if !s.executor.layers && s.executor.useCache {
|
|
||||||
err := ib.Run(step, s, noRunsRemaining)
|
err := ib.Run(step, s, noRunsRemaining)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, errors.Wrapf(err, "error building at step %+v", *step)
|
logrus.Debugf("%v", errors.Wrapf(err, "error building at step %+v", *step))
|
||||||
|
return "", nil, errors.Wrapf(err, "error building at STEP \"%s\"", step.Message)
|
||||||
|
}
|
||||||
|
if i < len(children)-1 {
|
||||||
|
// There are still more instructions to process
|
||||||
|
// for this stage. Make a note of the
|
||||||
|
// instruction in the history that we'll write
|
||||||
|
// for the image when we eventually commit it.
|
||||||
|
now := time.Now()
|
||||||
|
s.builder.AddPrependedEmptyLayer(&now, getCreatedBy(node), "", "")
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// This is the last instruction for this stage,
|
||||||
|
// so we should commit this container to create
|
||||||
|
// an image.
|
||||||
|
logCommit(s.output, i)
|
||||||
|
imgID, ref, err = s.commit(ctx, ib, getCreatedBy(node), s.output)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step)
|
||||||
|
}
|
||||||
|
logImageID(imgID)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if i < len(children)-1 {
|
// We're in a multi-layered build.
|
||||||
commitName = ""
|
var (
|
||||||
} else {
|
commitName string
|
||||||
|
cacheID string
|
||||||
|
err error
|
||||||
|
rebase bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// If we have to commit for this instruction, only assign the
|
||||||
|
// stage's configured output name to the last layer.
|
||||||
|
if i == len(children)-1 {
|
||||||
commitName = s.output
|
commitName = s.output
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this makes the tests happy, but it shouldn't be
|
|
||||||
// necessary unless this is the final stage.
|
|
||||||
commitName = s.executor.output
|
|
||||||
|
|
||||||
var (
|
|
||||||
cacheID string
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
// If we're using the cache, and we've managed to stick with
|
// If we're using the cache, and we've managed to stick with
|
||||||
// cached images so far, look for one that matches what we
|
// cached images so far, look for one that matches what we
|
||||||
// expect to produce for this instruction.
|
// expect to produce for this instruction.
|
||||||
if checkForLayers && s.executor.useCache {
|
// Only check at steps where we commit, so that we don't
|
||||||
|
// abandon the cache at this step just because we can't find an
|
||||||
|
// image with a history entry in it that we wouldn't have
|
||||||
|
// committed.
|
||||||
|
if checkForLayers && (s.stepRequiresCommit(step) || i == len(children)-1) && !(s.executor.squash && i == len(children)-1 && s.index == s.stages-1) {
|
||||||
cacheID, err = s.layerExists(ctx, node, children[:i])
|
cacheID, err = s.layerExists(ctx, node, children[:i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, errors.Wrap(err, "error checking if cached image exists from a previous build")
|
return "", nil, errors.Wrap(err, "error checking if cached image exists from a previous build")
|
||||||
}
|
}
|
||||||
}
|
if cacheID != "" {
|
||||||
if cacheID != "" {
|
// Note the cache hit.
|
||||||
fmt.Fprintf(s.executor.out, "--> Using cache %s\n", cacheID)
|
fmt.Fprintf(s.executor.out, "--> Using cache %s\n", cacheID)
|
||||||
}
|
} else {
|
||||||
|
// We're not going to find any more cache hits.
|
||||||
// If a cache is found and we're on the last step, that means
|
checkForLayers = false
|
||||||
// nothing in this phase changed. Just create a copy of the
|
|
||||||
// existing image and save it with the name that we were going
|
|
||||||
// to assign to the one that we were building, and make sure
|
|
||||||
// that the builder's root fs matches it.
|
|
||||||
if cacheID != "" && i == len(children)-1 {
|
|
||||||
if imgID, ref, err = s.copyExistingImage(ctx, cacheID, commitName); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we didn't find a cached step that we could just reuse,
|
if cacheID != "" {
|
||||||
// process the instruction and commit the layer.
|
// A suitable cached image was found, so just reuse it.
|
||||||
if cacheID == "" || !checkForLayers {
|
// If we need to name the resulting image because it's
|
||||||
checkForLayers = false
|
// the last step in this stage, add the name to the
|
||||||
|
// image.
|
||||||
|
imgID = cacheID
|
||||||
|
if commitName != "" && (s.stepRequiresCommit(step) || i == len(children)-1) {
|
||||||
|
logCommit(s.output, i)
|
||||||
|
if imgID, ref, err = s.copyExistingImage(ctx, cacheID, commitName); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
logImageID(imgID)
|
||||||
|
}
|
||||||
|
// Update our working container to be based off of the
|
||||||
|
// cached image, in case we need to read content from
|
||||||
|
// its root filesystem.
|
||||||
|
rebase = true
|
||||||
|
} else {
|
||||||
|
// If we didn't find a cached image that we could just reuse,
|
||||||
|
// process the instruction directly.
|
||||||
err := ib.Run(step, s, noRunsRemaining)
|
err := ib.Run(step, s, noRunsRemaining)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, errors.Wrapf(err, "error building at step %+v", *step)
|
logrus.Debugf("%v", errors.Wrapf(err, "error building at step %+v", *step))
|
||||||
|
return "", nil, errors.Wrapf(err, "error building at STEP \"%s\"", step.Message)
|
||||||
|
}
|
||||||
|
if s.stepRequiresCommit(step) || i == len(children)-1 {
|
||||||
|
// Either this is the last instruction, or
|
||||||
|
// there are more instructions and we need to
|
||||||
|
// create a layer from this one before
|
||||||
|
// continuing.
|
||||||
|
// TODO: only commit for the last instruction
|
||||||
|
// case if we need to use this stage's image as
|
||||||
|
// a base image later, or if we're the final
|
||||||
|
// stage.
|
||||||
|
logCommit(s.output, i)
|
||||||
|
imgID, ref, err = s.commit(ctx, ib, getCreatedBy(node), commitName)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step)
|
||||||
|
}
|
||||||
|
logImageID(imgID)
|
||||||
|
// We only need to build a new container rootfs
|
||||||
|
// using this image if we plan on making
|
||||||
|
// further changes to it. Subsequent stages
|
||||||
|
// that just want to use the rootfs as a source
|
||||||
|
// for COPY or ADD will be content with what we
|
||||||
|
// already have.
|
||||||
|
rebase = i < len(children)-1
|
||||||
|
} else {
|
||||||
|
// There are still more instructions to process
|
||||||
|
// for this stage, and we don't need to commit
|
||||||
|
// here. Make a note of the instruction in the
|
||||||
|
// history for the next commit.
|
||||||
|
now := time.Now()
|
||||||
|
s.builder.AddPrependedEmptyLayer(&now, getCreatedBy(node), "", "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit if no cache is found
|
if rebase {
|
||||||
if cacheID == "" {
|
// Since we either committed the working container or
|
||||||
imgID, ref, err = s.Commit(ctx, ib, getCreatedBy(node), commitName)
|
// are about to replace it with one based on a cached
|
||||||
if err != nil {
|
// image, add the current working container's ID to the
|
||||||
return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step)
|
// list of successful intermediate containers that
|
||||||
}
|
// we'll clean up later.
|
||||||
if i == len(children)-1 {
|
|
||||||
s.executor.log("COMMIT %s", commitName)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If we did find a cache, reuse the cached image's ID
|
|
||||||
// as the basis for the container for the next step.
|
|
||||||
imgID = cacheID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare for the next step with imgID as the new base image.
|
|
||||||
if i < len(children)-1 {
|
|
||||||
s.containerIDs = append(s.containerIDs, s.builder.ContainerID)
|
s.containerIDs = append(s.containerIDs, s.builder.ContainerID)
|
||||||
if err := s.Prepare(ctx, stage, imgID); err != nil {
|
|
||||||
|
// Prepare for the next step or subsequent phases by
|
||||||
|
// creating a new working container with the
|
||||||
|
// just-committed or updated cached image as its new
|
||||||
|
// base image.
|
||||||
|
// TODO: only create a new container if we know that
|
||||||
|
// we'll need the updated root filesystem.
|
||||||
|
if _, err := s.prepare(ctx, stage, imgID, false, true); err != nil {
|
||||||
return "", nil, errors.Wrap(err, "error preparing container for next step")
|
return "", nil, errors.Wrap(err, "error preparing container for next step")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.executor.layers { // print out the final imageID if we're using layers flag
|
|
||||||
fmt.Fprintf(s.executor.out, "--> %s\n", imgID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return imgID, ref, nil
|
return imgID, ref, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyExistingImage creates a copy of an image already in the store
|
// copyExistingImage creates a copy of an image already in the store
|
||||||
func (s *StageExecutor) copyExistingImage(ctx context.Context, cacheID, output string) (string, reference.Canonical, error) {
|
func (s *StageExecutor) copyExistingImage(ctx context.Context, cacheID, output string) (string, reference.Canonical, error) {
|
||||||
// Get the destination Image Reference
|
// If we don't need to attach a name to the image, just return the cache ID.
|
||||||
|
if output == "" {
|
||||||
|
return cacheID, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the destination image reference.
|
||||||
dest, err := s.executor.resolveNameToImageRef(output)
|
dest, err := s.executor.resolveNameToImageRef(output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
@ -1026,7 +1169,6 @@ func (s *StageExecutor) copyExistingImage(ctx context.Context, cacheID, output s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, errors.Wrapf(err, "error locating new copy of image %q (i.e., %q)", cacheID, transports.ImageName(dest))
|
return "", nil, errors.Wrapf(err, "error locating new copy of image %q (i.e., %q)", cacheID, transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
s.executor.log("COMMIT %s", s.output)
|
|
||||||
var ref reference.Canonical
|
var ref reference.Canonical
|
||||||
if dref := dest.DockerReference(); dref != nil {
|
if dref := dest.DockerReference(); dref != nil {
|
||||||
if ref, err = reference.WithDigest(dref, manifestDigest); err != nil {
|
if ref, err = reference.WithDigest(dref, manifestDigest); err != nil {
|
||||||
@ -1094,6 +1236,9 @@ func (b *Executor) getImageHistory(ctx context.Context, imageID string) ([]v1.Hi
|
|||||||
|
|
||||||
// getCreatedBy returns the command the image at node will be created by.
|
// getCreatedBy returns the command the image at node will be created by.
|
||||||
func getCreatedBy(node *parser.Node) string {
|
func getCreatedBy(node *parser.Node) string {
|
||||||
|
if node == nil {
|
||||||
|
return "/bin/sh"
|
||||||
|
}
|
||||||
if node.Value == "run" {
|
if node.Value == "run" {
|
||||||
return "/bin/sh -c " + node.Original[4:]
|
return "/bin/sh -c " + node.Original[4:]
|
||||||
}
|
}
|
||||||
@ -1201,12 +1346,16 @@ func urlContentModified(url string, historyTime *time.Time) (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit writes the container's contents to an image, using a passed-in tag as
|
// commit writes the container's contents to an image, using a passed-in tag as
|
||||||
// the name if there is one, generating a unique ID-based one otherwise.
|
// the name if there is one, generating a unique ID-based one otherwise.
|
||||||
func (s *StageExecutor) Commit(ctx context.Context, ib *imagebuilder.Builder, createdBy, output string) (string, reference.Canonical, error) {
|
func (s *StageExecutor) commit(ctx context.Context, ib *imagebuilder.Builder, createdBy, output string) (string, reference.Canonical, error) {
|
||||||
imageRef, err := s.executor.resolveNameToImageRef(output)
|
var imageRef types.ImageReference
|
||||||
if err != nil {
|
if output != "" {
|
||||||
return "", nil, err
|
imageRef2, err := s.executor.resolveNameToImageRef(output)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
imageRef = imageRef2
|
||||||
}
|
}
|
||||||
|
|
||||||
if ib.Author != "" {
|
if ib.Author != "" {
|
||||||
@ -1274,14 +1423,8 @@ func (s *StageExecutor) Commit(ctx context.Context, ib *imagebuilder.Builder, cr
|
|||||||
if imageRef != nil {
|
if imageRef != nil {
|
||||||
logName := transports.ImageName(imageRef)
|
logName := transports.ImageName(imageRef)
|
||||||
logrus.Debugf("COMMIT %q", logName)
|
logrus.Debugf("COMMIT %q", logName)
|
||||||
if !s.executor.quiet && !s.executor.layers && s.executor.useCache {
|
|
||||||
s.executor.log("COMMIT %s", logName)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
logrus.Debugf("COMMIT")
|
logrus.Debugf("COMMIT")
|
||||||
if !s.executor.quiet && !s.executor.layers && s.executor.useCache {
|
|
||||||
s.executor.log("COMMIT")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
writer := s.executor.reportWriter
|
writer := s.executor.reportWriter
|
||||||
if s.executor.layers || !s.executor.useCache {
|
if s.executor.layers || !s.executor.useCache {
|
||||||
@ -1294,7 +1437,6 @@ func (s *StageExecutor) Commit(ctx context.Context, ib *imagebuilder.Builder, cr
|
|||||||
ReportWriter: writer,
|
ReportWriter: writer,
|
||||||
PreferredManifestType: s.executor.outputFormat,
|
PreferredManifestType: s.executor.outputFormat,
|
||||||
SystemContext: s.executor.systemContext,
|
SystemContext: s.executor.systemContext,
|
||||||
IIDFile: s.executor.iidfile,
|
|
||||||
Squash: s.executor.squash,
|
Squash: s.executor.squash,
|
||||||
BlobDirectory: s.executor.blobDirectory,
|
BlobDirectory: s.executor.blobDirectory,
|
||||||
Parent: s.builder.FromImageID,
|
Parent: s.builder.FromImageID,
|
||||||
@ -1303,13 +1445,12 @@ func (s *StageExecutor) Commit(ctx context.Context, ib *imagebuilder.Builder, cr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
if options.IIDFile == "" && imgID != "" {
|
|
||||||
fmt.Fprintf(s.executor.out, "--> %s\n", imgID)
|
|
||||||
}
|
|
||||||
var ref reference.Canonical
|
var ref reference.Canonical
|
||||||
if dref := imageRef.DockerReference(); dref != nil {
|
if imageRef != nil {
|
||||||
if ref, err = reference.WithDigest(dref, manifestDigest); err != nil {
|
if dref := imageRef.DockerReference(); dref != nil {
|
||||||
return "", nil, errors.Wrapf(err, "error computing canonical reference for new image %q", imgID)
|
if ref, err = reference.WithDigest(dref, manifestDigest); err != nil {
|
||||||
|
return "", nil, errors.Wrapf(err, "error computing canonical reference for new image %q", imgID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return imgID, ref, nil
|
return imgID, ref, nil
|
||||||
@ -1321,10 +1462,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
|
|||||||
if len(stages) == 0 {
|
if len(stages) == 0 {
|
||||||
return "", nil, errors.New("error building: no stages to build")
|
return "", nil, errors.New("error building: no stages to build")
|
||||||
}
|
}
|
||||||
var (
|
var cleanupImages []string
|
||||||
stageExecutor *StageExecutor
|
|
||||||
cleanupImages []string
|
|
||||||
)
|
|
||||||
cleanupStages := make(map[int]*StageExecutor)
|
cleanupStages := make(map[int]*StageExecutor)
|
||||||
|
|
||||||
cleanup := func() error {
|
cleanup := func() error {
|
||||||
@ -1339,6 +1477,14 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
cleanupStages = nil
|
cleanupStages = nil
|
||||||
|
// Clean up any builders that we used to get data from images.
|
||||||
|
for _, builder := range b.containerMap {
|
||||||
|
if err := builder.Delete(); err != nil {
|
||||||
|
logrus.Debugf("Failed to cleanup image containers: %v", err)
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.containerMap = nil
|
||||||
// Clean up any intermediate containers associated with stages,
|
// Clean up any intermediate containers associated with stages,
|
||||||
// since we're not keeping them for debugging.
|
// since we're not keeping them for debugging.
|
||||||
if b.removeIntermediateCtrs {
|
if b.removeIntermediateCtrs {
|
||||||
@ -1382,37 +1528,44 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
|
|||||||
output = b.output
|
output = b.output
|
||||||
}
|
}
|
||||||
|
|
||||||
stageExecutor = b.startStage(stage.Name, stage.Position, len(stages), base, output)
|
stageExecutor := b.startStage(stage.Name, stage.Position, len(stages), base, output)
|
||||||
if err := stageExecutor.Prepare(ctx, stage, base); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always remove the intermediate/build containers, even if the build was unsuccessful.
|
// If this a single-layer build, or if it's a multi-layered
|
||||||
// If building with layers, remove all intermediate/build containers if b.forceRmIntermediateCtrs
|
// build and b.forceRmIntermediateCtrs is set, make sure we
|
||||||
// is true.
|
// remove the intermediate/build containers, regardless of
|
||||||
|
// whether or not the stage's build fails.
|
||||||
if b.forceRmIntermediateCtrs || !b.layers {
|
if b.forceRmIntermediateCtrs || !b.layers {
|
||||||
cleanupStages[stage.Position] = stageExecutor
|
cleanupStages[stage.Position] = stageExecutor
|
||||||
}
|
}
|
||||||
if imageID, ref, err = stageExecutor.Execute(ctx, stage); err != nil {
|
|
||||||
|
// Build this stage.
|
||||||
|
if imageID, ref, err = stageExecutor.Execute(ctx, stage, base); err != nil {
|
||||||
lastErr = err
|
lastErr = err
|
||||||
}
|
}
|
||||||
if lastErr != nil {
|
if lastErr != nil {
|
||||||
return "", nil, lastErr
|
return "", nil, lastErr
|
||||||
}
|
}
|
||||||
if !b.forceRmIntermediateCtrs && b.removeIntermediateCtrs {
|
|
||||||
|
// The stage succeeded, so remove its build container if we're
|
||||||
|
// told to delete successful intermediate/build containers for
|
||||||
|
// multi-layered builds.
|
||||||
|
if b.removeIntermediateCtrs {
|
||||||
cleanupStages[stage.Position] = stageExecutor
|
cleanupStages[stage.Position] = stageExecutor
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is an intermediate stage, make a note to remove its
|
// If this is an intermediate stage, make a note of the ID, so
|
||||||
// image later.
|
// that we can look it up later.
|
||||||
if _, err := strconv.Atoi(stage.Name); err != nil {
|
if stageIndex < len(stages)-1 {
|
||||||
if imageID, ref, err = stageExecutor.Commit(ctx, stages[stageIndex].Builder, "", output); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
b.imageMap[stage.Name] = imageID
|
b.imageMap[stage.Name] = imageID
|
||||||
cleanupImages = append(cleanupImages, imageID)
|
// We're not populating the cache with intermediate
|
||||||
|
// images, so add this one to the list of images that
|
||||||
|
// we'll remove later.
|
||||||
|
if !b.layers {
|
||||||
|
cleanupImages = append(cleanupImages, imageID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b.unusedArgs) > 0 {
|
if len(b.unusedArgs) > 0 {
|
||||||
unusedList := make([]string, 0, len(b.unusedArgs))
|
unusedList := make([]string, 0, len(b.unusedArgs))
|
||||||
for k := range b.unusedArgs {
|
for k := range b.unusedArgs {
|
||||||
@ -1422,25 +1575,16 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
|
|||||||
fmt.Fprintf(b.out, "[Warning] one or more build args were not consumed: %v\n", unusedList)
|
fmt.Fprintf(b.out, "[Warning] one or more build args were not consumed: %v\n", unusedList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have a one line Dockerfile (i.e., single phase, no
|
|
||||||
// actual steps) making layers irrelevant, or the user told us to
|
|
||||||
// ignore layers.
|
|
||||||
singleLineDockerfile := (len(stages) < 2 && len(stages[0].Node.Children) < 1)
|
|
||||||
ignoreLayers := singleLineDockerfile || !b.layers && b.useCache
|
|
||||||
|
|
||||||
if ignoreLayers {
|
|
||||||
if imageID, ref, err = stageExecutor.Commit(ctx, stages[len(stages)-1].Builder, "", b.output); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
if singleLineDockerfile {
|
|
||||||
b.log("COMMIT %s", ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cleanup(); err != nil {
|
if err := cleanup(); err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.iidfile != "" {
|
||||||
|
if err = ioutil.WriteFile(b.iidfile, []byte(imageID), 0644); err != nil {
|
||||||
|
return imageID, ref, errors.Wrapf(err, "failed to write image ID to file %q", b.iidfile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return imageID, ref, nil
|
return imageID, ref, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1516,8 +1660,6 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOpt
|
|||||||
dockerfiles = append(dockerfiles, data)
|
dockerfiles = append(dockerfiles, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
dockerfiles = processCopyFrom(dockerfiles)
|
|
||||||
|
|
||||||
mainNode, err := imagebuilder.ParseDockerfile(dockerfiles[0])
|
mainNode, err := imagebuilder.ParseDockerfile(dockerfiles[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, errors.Wrapf(err, "error parsing main Dockerfile")
|
return "", nil, errors.Wrapf(err, "error parsing main Dockerfile")
|
||||||
@ -1548,79 +1690,6 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOpt
|
|||||||
return exec.Build(ctx, stages)
|
return exec.Build(ctx, stages)
|
||||||
}
|
}
|
||||||
|
|
||||||
// processCopyFrom goes through the Dockerfiles and handles any 'COPY --from' instances
|
|
||||||
// prepending a new FROM statement the Dockerfile that do not already have a corresponding
|
|
||||||
// FROM command within them.
|
|
||||||
func processCopyFrom(dockerfiles []io.ReadCloser) []io.ReadCloser {
|
|
||||||
var newDockerfiles []io.ReadCloser
|
|
||||||
// fromMap contains the names of the images seen in a FROM
|
|
||||||
// line in the Dockerfiles. The boolean value just completes the map object.
|
|
||||||
fromMap := make(map[string]bool)
|
|
||||||
// asMap contains the names of the images seen after a "FROM image AS"
|
|
||||||
// line in the Dockefiles. The boolean value just completes the map object.
|
|
||||||
asMap := make(map[string]bool)
|
|
||||||
|
|
||||||
copyRE := regexp.MustCompile(`\s*COPY\s+--from=`)
|
|
||||||
fromRE := regexp.MustCompile(`\s*FROM\s+`)
|
|
||||||
asRE := regexp.MustCompile(`(?i)\s+as\s+`)
|
|
||||||
for _, dfile := range dockerfiles {
|
|
||||||
if dfileBinary, err := ioutil.ReadAll(dfile); err == nil {
|
|
||||||
dfileString := fmt.Sprintf("%s", dfileBinary)
|
|
||||||
copyFromContent := copyRE.Split(dfileString, -1)
|
|
||||||
// no "COPY --from=", just continue
|
|
||||||
if len(copyFromContent) < 2 {
|
|
||||||
newDockerfiles = append(newDockerfiles, ioutil.NopCloser(strings.NewReader(dfileString)))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Load all image names in our Dockerfiles into a map
|
|
||||||
// for easy reference later.
|
|
||||||
fromContent := fromRE.Split(dfileString, -1)
|
|
||||||
for i := 0; i < len(fromContent); i++ {
|
|
||||||
imageName := strings.Split(fromContent[i], " ")
|
|
||||||
if len(imageName) > 0 {
|
|
||||||
finalImage := strings.Split(imageName[0], "\n")
|
|
||||||
if finalImage[0] != "" {
|
|
||||||
fromMap[strings.TrimSpace(finalImage[0])] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logrus.Debug("fromMap: ", fromMap)
|
|
||||||
|
|
||||||
// Load all image names associated with an 'as' or 'AS' in
|
|
||||||
// our Dockerfiles into a map for easy reference later.
|
|
||||||
asContent := asRE.Split(dfileString, -1)
|
|
||||||
// Skip the first entry in the array as it's stuff before
|
|
||||||
// the " as " and we don't care.
|
|
||||||
for i := 1; i < len(asContent); i++ {
|
|
||||||
asName := strings.Split(asContent[i], " ")
|
|
||||||
if len(asName) > 0 {
|
|
||||||
finalAsImage := strings.Split(asName[0], "\n")
|
|
||||||
if finalAsImage[0] != "" {
|
|
||||||
asMap[strings.TrimSpace(finalAsImage[0])] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logrus.Debug("asMap: ", asMap)
|
|
||||||
|
|
||||||
for i := 1; i < len(copyFromContent); i++ {
|
|
||||||
fromArray := strings.Split(copyFromContent[i], " ")
|
|
||||||
// If the image isn't a stage number or already declared,
|
|
||||||
// add a FROM statement for it to the top of our Dockerfile.
|
|
||||||
trimmedFrom := strings.TrimSpace(fromArray[0])
|
|
||||||
_, okFrom := fromMap[trimmedFrom]
|
|
||||||
_, okAs := asMap[trimmedFrom]
|
|
||||||
_, err := strconv.Atoi(trimmedFrom)
|
|
||||||
if !okFrom && !okAs && err != nil {
|
|
||||||
from := "FROM " + trimmedFrom
|
|
||||||
newDockerfiles = append(newDockerfiles, ioutil.NopCloser(strings.NewReader(from)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newDockerfiles = append(newDockerfiles, ioutil.NopCloser(strings.NewReader(dfileString)))
|
|
||||||
} // End if dfileBinary, err := ioutil.ReadAll(dfile); err == nil
|
|
||||||
} // End for _, dfile := range dockerfiles {
|
|
||||||
return newDockerfiles
|
|
||||||
}
|
|
||||||
|
|
||||||
// deleteSuccessfulIntermediateCtrs goes through the container IDs in each
|
// deleteSuccessfulIntermediateCtrs goes through the container IDs in each
|
||||||
// stage's containerIDs list and deletes the containers associated with those
|
// stage's containerIDs list and deletes the containers associated with those
|
||||||
// IDs.
|
// IDs.
|
||||||
|
12
vendor/github.com/containers/buildah/imagebuildah/util.go
generated
vendored
12
vendor/github.com/containers/buildah/imagebuildah/util.go
generated
vendored
@ -105,6 +105,18 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err
|
|||||||
return "", "", errors.Errorf("unreachable code reached")
|
return "", "", errors.Errorf("unreachable code reached")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dedupeStringSlice(slice []string) []string {
|
||||||
|
done := make([]string, 0, len(slice))
|
||||||
|
m := make(map[string]struct{})
|
||||||
|
for _, s := range slice {
|
||||||
|
if _, present := m[s]; !present {
|
||||||
|
m[s] = struct{}{}
|
||||||
|
done = append(done, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return done
|
||||||
|
}
|
||||||
|
|
||||||
// InitReexec is a wrapper for buildah.InitReexec(). It should be called at
|
// InitReexec is a wrapper for buildah.InitReexec(). It should be called at
|
||||||
// the start of main(), and if it returns true, main() should return
|
// the start of main(), and if it returns true, main() should return
|
||||||
// immediately.
|
// immediately.
|
||||||
|
2
vendor/github.com/containers/buildah/info.go
generated
vendored
2
vendor/github.com/containers/buildah/info.go
generated
vendored
@ -11,7 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/buildah/unshare"
|
"github.com/containers/buildah/pkg/unshare"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/system"
|
"github.com/containers/storage/pkg/system"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
16
vendor/github.com/containers/buildah/pkg/cli/common.go
generated
vendored
16
vendor/github.com/containers/buildah/pkg/cli/common.go
generated
vendored
@ -86,6 +86,9 @@ type FromAndBudResults struct {
|
|||||||
CPUSetCPUs string
|
CPUSetCPUs string
|
||||||
CPUSetMems string
|
CPUSetMems string
|
||||||
CPUShares uint64
|
CPUShares uint64
|
||||||
|
DNSSearch []string
|
||||||
|
DNSServers []string
|
||||||
|
DNSOptions []string
|
||||||
Isolation string
|
Isolation string
|
||||||
Memory string
|
Memory string
|
||||||
MemorySwap string
|
MemorySwap string
|
||||||
@ -132,9 +135,9 @@ func GetLayerFlags(flags *LayerResults) pflag.FlagSet {
|
|||||||
// GetBudFlags returns common bud flags
|
// GetBudFlags returns common bud flags
|
||||||
func GetBudFlags(flags *BudResults) pflag.FlagSet {
|
func GetBudFlags(flags *BudResults) pflag.FlagSet {
|
||||||
fs := pflag.FlagSet{}
|
fs := pflag.FlagSet{}
|
||||||
fs.StringSliceVar(&flags.Annotation, "annotation", []string{}, "Set metadata for an image (default [])")
|
fs.StringArrayVar(&flags.Annotation, "annotation", []string{}, "Set metadata for an image (default [])")
|
||||||
fs.StringVar(&flags.Authfile, "authfile", "", "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json")
|
fs.StringVar(&flags.Authfile, "authfile", "", "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json")
|
||||||
fs.StringSliceVar(&flags.BuildArg, "build-arg", []string{}, "`argument=value` to supply to the builder")
|
fs.StringArrayVar(&flags.BuildArg, "build-arg", []string{}, "`argument=value` to supply to the builder")
|
||||||
fs.StringVar(&flags.CacheFrom, "cache-from", "", "Images to utilise as potential cache sources. The build process does not currently support caching so this is a NOOP.")
|
fs.StringVar(&flags.CacheFrom, "cache-from", "", "Images to utilise as potential cache sources. The build process does not currently support caching so this is a NOOP.")
|
||||||
fs.StringVar(&flags.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry")
|
fs.StringVar(&flags.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry")
|
||||||
fs.BoolVar(&flags.Compress, "compress", false, "This is legacy option, which has no effect on the image")
|
fs.BoolVar(&flags.Compress, "compress", false, "This is legacy option, which has no effect on the image")
|
||||||
@ -144,7 +147,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
|
|||||||
fs.StringSliceVarP(&flags.File, "file", "f", []string{}, "`pathname or URL` of a Dockerfile")
|
fs.StringSliceVarP(&flags.File, "file", "f", []string{}, "`pathname or URL` of a Dockerfile")
|
||||||
fs.StringVar(&flags.Format, "format", DefaultFormat(), "`format` of the built image's manifest and metadata. Use BUILDAH_FORMAT environment variable to override.")
|
fs.StringVar(&flags.Format, "format", DefaultFormat(), "`format` of the built image's manifest and metadata. Use BUILDAH_FORMAT environment variable to override.")
|
||||||
fs.StringVar(&flags.Iidfile, "iidfile", "", "`file` to write the image ID to")
|
fs.StringVar(&flags.Iidfile, "iidfile", "", "`file` to write the image ID to")
|
||||||
fs.StringSliceVar(&flags.Label, "label", []string{}, "Set metadata for an image (default [])")
|
fs.StringArrayVar(&flags.Label, "label", []string{}, "Set metadata for an image (default [])")
|
||||||
fs.BoolVar(&flags.NoCache, "no-cache", false, "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.")
|
fs.BoolVar(&flags.NoCache, "no-cache", false, "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.")
|
||||||
fs.StringVar(&flags.Logfile, "logfile", "", "log to `file` instead of stdout/stderr")
|
fs.StringVar(&flags.Logfile, "logfile", "", "log to `file` instead of stdout/stderr")
|
||||||
fs.IntVar(&flags.Loglevel, "loglevel", 0, "adjust logging level (range from -2 to 3)")
|
fs.IntVar(&flags.Loglevel, "loglevel", 0, "adjust logging level (range from -2 to 3)")
|
||||||
@ -157,7 +160,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
|
|||||||
fs.StringSliceVar(&flags.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime")
|
fs.StringSliceVar(&flags.RuntimeFlags, "runtime-flag", []string{}, "add global flags for the container runtime")
|
||||||
fs.StringVar(&flags.SignaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
|
fs.StringVar(&flags.SignaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
|
||||||
fs.BoolVar(&flags.Squash, "squash", false, "Squash newly built layers into a single new layer.")
|
fs.BoolVar(&flags.Squash, "squash", false, "Squash newly built layers into a single new layer.")
|
||||||
fs.StringSliceVarP(&flags.Tag, "tag", "t", []string{}, "tagged `name` to apply to the built image")
|
fs.StringArrayVarP(&flags.Tag, "tag", "t", []string{}, "tagged `name` to apply to the built image")
|
||||||
fs.StringVar(&flags.Target, "target", "", "set the target build stage to build")
|
fs.StringVar(&flags.Target, "target", "", "set the target build stage to build")
|
||||||
fs.BoolVar(&flags.TlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")
|
fs.BoolVar(&flags.TlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")
|
||||||
return fs
|
return fs
|
||||||
@ -176,10 +179,13 @@ func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults,
|
|||||||
fs.Uint64VarP(&flags.CPUShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
|
fs.Uint64VarP(&flags.CPUShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
|
||||||
fs.StringVar(&flags.CPUSetCPUs, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
|
fs.StringVar(&flags.CPUSetCPUs, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
|
||||||
fs.StringVar(&flags.CPUSetMems, "cpuset-mems", "", "memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.")
|
fs.StringVar(&flags.CPUSetMems, "cpuset-mems", "", "memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.")
|
||||||
|
fs.StringSliceVar(&flags.DNSSearch, "dns-search", []string{}, "Set custom DNS search domains")
|
||||||
|
fs.StringSliceVar(&flags.DNSServers, "dns", []string{}, "Set custom DNS servers")
|
||||||
|
fs.StringSliceVar(&flags.DNSOptions, "dns-option", []string{}, "Set custom DNS options")
|
||||||
fs.StringVar(&flags.Isolation, "isolation", DefaultIsolation(), "`type` of process isolation to use. Use BUILDAH_ISOLATION environment variable to override.")
|
fs.StringVar(&flags.Isolation, "isolation", DefaultIsolation(), "`type` of process isolation to use. Use BUILDAH_ISOLATION environment variable to override.")
|
||||||
fs.StringVarP(&flags.Memory, "memory", "m", "", "memory limit (format: <number>[<unit>], where unit = b, k, m or g)")
|
fs.StringVarP(&flags.Memory, "memory", "m", "", "memory limit (format: <number>[<unit>], where unit = b, k, m or g)")
|
||||||
fs.StringVar(&flags.MemorySwap, "memory-swap", "", "swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
fs.StringVar(&flags.MemorySwap, "memory-swap", "", "swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
||||||
fs.StringSliceVar(&flags.SecurityOpt, "security-opt", []string{}, "security options (default [])")
|
fs.StringArrayVar(&flags.SecurityOpt, "security-opt", []string{}, "security options (default [])")
|
||||||
fs.StringVar(&flags.ShmSize, "shm-size", "65536k", "size of '/dev/shm'. The format is `<number><unit>`.")
|
fs.StringVar(&flags.ShmSize, "shm-size", "65536k", "size of '/dev/shm'. The format is `<number><unit>`.")
|
||||||
fs.StringSliceVar(&flags.Ulimit, "ulimit", []string{}, "ulimit options (default [])")
|
fs.StringSliceVar(&flags.Ulimit, "ulimit", []string{}, "ulimit options (default [])")
|
||||||
fs.StringSliceVarP(&flags.Volume, "volume", "v", []string{}, "bind mount a volume into the container (default [])")
|
fs.StringSliceVarP(&flags.Volume, "volume", "v", []string{}, "bind mount a volume into the container (default [])")
|
||||||
|
12
vendor/github.com/containers/buildah/pkg/parse/parse.go
generated
vendored
12
vendor/github.com/containers/buildah/pkg/parse/parse.go
generated
vendored
@ -6,7 +6,6 @@ package parse
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -21,6 +20,7 @@ import (
|
|||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
@ -71,6 +71,11 @@ func CommonBuildOptions(c *cobra.Command) (*buildah.CommonBuildOptions, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dnsServers, _ := c.Flags().GetStringSlice("dns")
|
||||||
|
dnsSearch, _ := c.Flags().GetStringSlice("dns-search")
|
||||||
|
dnsOptions, _ := c.Flags().GetStringSlice("dns-option")
|
||||||
|
|
||||||
if _, err := units.FromHumanSize(c.Flag("shm-size").Value.String()); err != nil {
|
if _, err := units.FromHumanSize(c.Flag("shm-size").Value.String()); err != nil {
|
||||||
return nil, errors.Wrapf(err, "invalid --shm-size")
|
return nil, errors.Wrapf(err, "invalid --shm-size")
|
||||||
}
|
}
|
||||||
@ -90,13 +95,16 @@ func CommonBuildOptions(c *cobra.Command) (*buildah.CommonBuildOptions, error) {
|
|||||||
CPUSetCPUs: c.Flag("cpuset-cpus").Value.String(),
|
CPUSetCPUs: c.Flag("cpuset-cpus").Value.String(),
|
||||||
CPUSetMems: c.Flag("cpuset-mems").Value.String(),
|
CPUSetMems: c.Flag("cpuset-mems").Value.String(),
|
||||||
CPUShares: cpuShares,
|
CPUShares: cpuShares,
|
||||||
|
DNSSearch: dnsSearch,
|
||||||
|
DNSServers: dnsServers,
|
||||||
|
DNSOptions: dnsOptions,
|
||||||
Memory: memoryLimit,
|
Memory: memoryLimit,
|
||||||
MemorySwap: memorySwap,
|
MemorySwap: memorySwap,
|
||||||
ShmSize: c.Flag("shm-size").Value.String(),
|
ShmSize: c.Flag("shm-size").Value.String(),
|
||||||
Ulimit: append(defaultLimits, ulimit...),
|
Ulimit: append(defaultLimits, ulimit...),
|
||||||
Volumes: volumes,
|
Volumes: volumes,
|
||||||
}
|
}
|
||||||
securityOpts, _ := c.Flags().GetStringSlice("security-opt")
|
securityOpts, _ := c.Flags().GetStringArray("security-opt")
|
||||||
if err := parseSecurityOpts(securityOpts, commonOpts); err != nil {
|
if err := parseSecurityOpts(securityOpts, commonOpts); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
static const char *_max_user_namespaces = "/proc/sys/user/max_user_namespaces";
|
static const char *_max_user_namespaces = "/proc/sys/user/max_user_namespaces";
|
||||||
static const char *_unprivileged_user_namespaces = "/proc/sys/kernel/unprivileged_userns_clone";
|
static const char *_unprivileged_user_namespaces = "/proc/sys/kernel/unprivileged_userns_clone";
|
||||||
|
|
||||||
static int _buildah_unshare_parse_envint(const char *envname) {
|
static int _containers_unshare_parse_envint(const char *envname) {
|
||||||
char *p, *q;
|
char *p, *q;
|
||||||
long l;
|
long l;
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ static char **parse_proc_stringlist(const char *list) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int buildah_reexec(void) {
|
static int containers_reexec(void) {
|
||||||
char **argv, *exename;
|
char **argv, *exename;
|
||||||
int fd, mmfd, n_read, n_written;
|
int fd, mmfd, n_read, n_written;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@ -196,12 +196,12 @@ static int buildah_reexec(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _buildah_unshare(void)
|
void _containers_unshare(void)
|
||||||
{
|
{
|
||||||
int flags, pidfd, continuefd, n, pgrp, sid, ctty;
|
int flags, pidfd, continuefd, n, pgrp, sid, ctty;
|
||||||
char buf[2048];
|
char buf[2048];
|
||||||
|
|
||||||
flags = _buildah_unshare_parse_envint("_Buildah-unshare");
|
flags = _containers_unshare_parse_envint("_Containers-unshare");
|
||||||
if (flags == -1) {
|
if (flags == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -213,7 +213,7 @@ void _buildah_unshare(void)
|
|||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pidfd = _buildah_unshare_parse_envint("_Buildah-pid-pipe");
|
pidfd = _containers_unshare_parse_envint("_Containers-pid-pipe");
|
||||||
if (pidfd != -1) {
|
if (pidfd != -1) {
|
||||||
snprintf(buf, sizeof(buf), "%llu", (unsigned long long) getpid());
|
snprintf(buf, sizeof(buf), "%llu", (unsigned long long) getpid());
|
||||||
size_t size = write(pidfd, buf, strlen(buf));
|
size_t size = write(pidfd, buf, strlen(buf));
|
||||||
@ -223,7 +223,7 @@ void _buildah_unshare(void)
|
|||||||
}
|
}
|
||||||
close(pidfd);
|
close(pidfd);
|
||||||
}
|
}
|
||||||
continuefd = _buildah_unshare_parse_envint("_Buildah-continue-pipe");
|
continuefd = _containers_unshare_parse_envint("_Containers-continue-pipe");
|
||||||
if (continuefd != -1) {
|
if (continuefd != -1) {
|
||||||
n = read(continuefd, buf, sizeof(buf));
|
n = read(continuefd, buf, sizeof(buf));
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
@ -232,21 +232,21 @@ void _buildah_unshare(void)
|
|||||||
}
|
}
|
||||||
close(continuefd);
|
close(continuefd);
|
||||||
}
|
}
|
||||||
sid = _buildah_unshare_parse_envint("_Buildah-setsid");
|
sid = _containers_unshare_parse_envint("_Containers-setsid");
|
||||||
if (sid == 1) {
|
if (sid == 1) {
|
||||||
if (setsid() == -1) {
|
if (setsid() == -1) {
|
||||||
fprintf(stderr, "Error during setsid: %m\n");
|
fprintf(stderr, "Error during setsid: %m\n");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pgrp = _buildah_unshare_parse_envint("_Buildah-setpgrp");
|
pgrp = _containers_unshare_parse_envint("_Containers-setpgrp");
|
||||||
if (pgrp == 1) {
|
if (pgrp == 1) {
|
||||||
if (setpgrp() == -1) {
|
if (setpgrp() == -1) {
|
||||||
fprintf(stderr, "Error during setpgrp: %m\n");
|
fprintf(stderr, "Error during setpgrp: %m\n");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctty = _buildah_unshare_parse_envint("_Buildah-ctty");
|
ctty = _containers_unshare_parse_envint("_Containers-ctty");
|
||||||
if (ctty != -1) {
|
if (ctty != -1) {
|
||||||
if (ioctl(ctty, TIOCSCTTY, 0) == -1) {
|
if (ioctl(ctty, TIOCSCTTY, 0) == -1) {
|
||||||
fprintf(stderr, "Error while setting controlling terminal to %d: %m\n", ctty);
|
fprintf(stderr, "Error while setting controlling terminal to %d: %m\n", ctty);
|
||||||
@ -269,7 +269,7 @@ void _buildah_unshare(void)
|
|||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (buildah_reexec() != 0) {
|
if (containers_reexec() != 0) {
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
@ -8,6 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/user"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -18,6 +19,8 @@ import (
|
|||||||
"github.com/containers/storage/pkg/reexec"
|
"github.com/containers/storage/pkg/reexec"
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/syndtr/gocapability/capability"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cmd wraps an exec.Cmd created by the reexec package in unshare(), and
|
// Cmd wraps an exec.Cmd created by the reexec package in unshare(), and
|
||||||
@ -54,7 +57,7 @@ func (c *Cmd) Start() error {
|
|||||||
if c.Env == nil {
|
if c.Env == nil {
|
||||||
c.Env = os.Environ()
|
c.Env = os.Environ()
|
||||||
}
|
}
|
||||||
c.Env = append(c.Env, fmt.Sprintf("_Buildah-unshare=%d", c.UnshareFlags))
|
c.Env = append(c.Env, fmt.Sprintf("_Containers-unshare=%d", c.UnshareFlags))
|
||||||
|
|
||||||
// Please the libpod "rootless" package to find the expected env variables.
|
// Please the libpod "rootless" package to find the expected env variables.
|
||||||
if os.Geteuid() != 0 {
|
if os.Geteuid() != 0 {
|
||||||
@ -67,7 +70,7 @@ func (c *Cmd) Start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error creating pid pipe")
|
return errors.Wrapf(err, "error creating pid pipe")
|
||||||
}
|
}
|
||||||
c.Env = append(c.Env, fmt.Sprintf("_Buildah-pid-pipe=%d", len(c.ExtraFiles)+3))
|
c.Env = append(c.Env, fmt.Sprintf("_Containers-pid-pipe=%d", len(c.ExtraFiles)+3))
|
||||||
c.ExtraFiles = append(c.ExtraFiles, pidWrite)
|
c.ExtraFiles = append(c.ExtraFiles, pidWrite)
|
||||||
|
|
||||||
// Create the pipe for letting the child know to proceed.
|
// Create the pipe for letting the child know to proceed.
|
||||||
@ -77,18 +80,18 @@ func (c *Cmd) Start() error {
|
|||||||
pidWrite.Close()
|
pidWrite.Close()
|
||||||
return errors.Wrapf(err, "error creating pid pipe")
|
return errors.Wrapf(err, "error creating pid pipe")
|
||||||
}
|
}
|
||||||
c.Env = append(c.Env, fmt.Sprintf("_Buildah-continue-pipe=%d", len(c.ExtraFiles)+3))
|
c.Env = append(c.Env, fmt.Sprintf("_Containers-continue-pipe=%d", len(c.ExtraFiles)+3))
|
||||||
c.ExtraFiles = append(c.ExtraFiles, continueRead)
|
c.ExtraFiles = append(c.ExtraFiles, continueRead)
|
||||||
|
|
||||||
// Pass along other instructions.
|
// Pass along other instructions.
|
||||||
if c.Setsid {
|
if c.Setsid {
|
||||||
c.Env = append(c.Env, "_Buildah-setsid=1")
|
c.Env = append(c.Env, "_Containers-setsid=1")
|
||||||
}
|
}
|
||||||
if c.Setpgrp {
|
if c.Setpgrp {
|
||||||
c.Env = append(c.Env, "_Buildah-setpgrp=1")
|
c.Env = append(c.Env, "_Containers-setpgrp=1")
|
||||||
}
|
}
|
||||||
if c.Ctty != nil {
|
if c.Ctty != nil {
|
||||||
c.Env = append(c.Env, fmt.Sprintf("_Buildah-ctty=%d", len(c.ExtraFiles)+3))
|
c.Env = append(c.Env, fmt.Sprintf("_Containers-ctty=%d", len(c.ExtraFiles)+3))
|
||||||
c.ExtraFiles = append(c.ExtraFiles, c.Ctty)
|
c.ExtraFiles = append(c.ExtraFiles, c.Ctty)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,3 +309,140 @@ func GetRootlessUID() int {
|
|||||||
func RootlessEnv() []string {
|
func RootlessEnv() []string {
|
||||||
return append(os.Environ(), UsernsEnvName+"=done")
|
return append(os.Environ(), UsernsEnvName+"=done")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Runnable interface {
|
||||||
|
Run() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func bailOnError(err error, format string, a ...interface{}) {
|
||||||
|
if err != nil {
|
||||||
|
if format != "" {
|
||||||
|
logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaybeReexecUsingUserNamespace re-exec the process in a new namespace
|
||||||
|
func MaybeReexecUsingUserNamespace(evenForRoot bool) {
|
||||||
|
// If we've already been through this once, no need to try again.
|
||||||
|
if os.Geteuid() == 0 && IsRootless() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var uidNum, gidNum uint64
|
||||||
|
// Figure out who we are.
|
||||||
|
me, err := user.Current()
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
bailOnError(err, "error determining current user")
|
||||||
|
uidNum, err = strconv.ParseUint(me.Uid, 10, 32)
|
||||||
|
bailOnError(err, "error parsing current UID %s", me.Uid)
|
||||||
|
gidNum, err = strconv.ParseUint(me.Gid, 10, 32)
|
||||||
|
bailOnError(err, "error parsing current GID %s", me.Gid)
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
// ID mappings to use to reexec ourselves.
|
||||||
|
var uidmap, gidmap []specs.LinuxIDMapping
|
||||||
|
if uidNum != 0 || evenForRoot {
|
||||||
|
// Read the set of ID mappings that we're allowed to use. Each
|
||||||
|
// range in /etc/subuid and /etc/subgid file is a starting host
|
||||||
|
// ID and a range size.
|
||||||
|
uidmap, gidmap, err = util.GetSubIDMappings(me.Username, me.Username)
|
||||||
|
bailOnError(err, "error reading allowed ID mappings")
|
||||||
|
if len(uidmap) == 0 {
|
||||||
|
logrus.Warnf("Found no UID ranges set aside for user %q in /etc/subuid.", me.Username)
|
||||||
|
}
|
||||||
|
if len(gidmap) == 0 {
|
||||||
|
logrus.Warnf("Found no GID ranges set aside for user %q in /etc/subgid.", me.Username)
|
||||||
|
}
|
||||||
|
// Map our UID and GID, then the subuid and subgid ranges,
|
||||||
|
// consecutively, starting at 0, to get the mappings to use for
|
||||||
|
// a copy of ourselves.
|
||||||
|
uidmap = append([]specs.LinuxIDMapping{{HostID: uint32(uidNum), ContainerID: 0, Size: 1}}, uidmap...)
|
||||||
|
gidmap = append([]specs.LinuxIDMapping{{HostID: uint32(gidNum), ContainerID: 0, Size: 1}}, gidmap...)
|
||||||
|
var rangeStart uint32
|
||||||
|
for i := range uidmap {
|
||||||
|
uidmap[i].ContainerID = rangeStart
|
||||||
|
rangeStart += uidmap[i].Size
|
||||||
|
}
|
||||||
|
rangeStart = 0
|
||||||
|
for i := range gidmap {
|
||||||
|
gidmap[i].ContainerID = rangeStart
|
||||||
|
rangeStart += gidmap[i].Size
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we have CAP_SYS_ADMIN, then we don't need to create a new namespace in order to be able
|
||||||
|
// to use unshare(), so don't bother creating a new user namespace at this point.
|
||||||
|
capabilities, err := capability.NewPid(0)
|
||||||
|
bailOnError(err, "error reading the current capabilities sets")
|
||||||
|
if capabilities.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Read the set of ID mappings that we're currently using.
|
||||||
|
uidmap, gidmap, err = util.GetHostIDMappings("")
|
||||||
|
bailOnError(err, "error reading current ID mappings")
|
||||||
|
// Just reuse them.
|
||||||
|
for i := range uidmap {
|
||||||
|
uidmap[i].HostID = uidmap[i].ContainerID
|
||||||
|
}
|
||||||
|
for i := range gidmap {
|
||||||
|
gidmap[i].HostID = gidmap[i].ContainerID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlike most uses of reexec or unshare, we're using a name that
|
||||||
|
// _won't_ be recognized as a registered reexec handler, since we
|
||||||
|
// _want_ to fall through reexec.Init() to the normal main().
|
||||||
|
cmd := Command(append([]string{fmt.Sprintf("%s-in-a-user-namespace", os.Args[0])}, os.Args[1:]...)...)
|
||||||
|
|
||||||
|
// If, somehow, we don't become UID 0 in our child, indicate that the child shouldn't try again.
|
||||||
|
err = os.Setenv(UsernsEnvName, "1")
|
||||||
|
bailOnError(err, "error setting %s=1 in environment", UsernsEnvName)
|
||||||
|
|
||||||
|
// Reuse our stdio.
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// Set up a new user namespace with the ID mapping.
|
||||||
|
cmd.UnshareFlags = syscall.CLONE_NEWUSER | syscall.CLONE_NEWNS
|
||||||
|
cmd.UseNewuidmap = uidNum != 0
|
||||||
|
cmd.UidMappings = uidmap
|
||||||
|
cmd.UseNewgidmap = uidNum != 0
|
||||||
|
cmd.GidMappings = gidmap
|
||||||
|
cmd.GidMappingsEnableSetgroups = true
|
||||||
|
|
||||||
|
// Finish up.
|
||||||
|
logrus.Debugf("running %+v with environment %+v, UID map %+v, and GID map %+v", cmd.Cmd.Args, os.Environ(), cmd.UidMappings, cmd.GidMappings)
|
||||||
|
ExecRunnable(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecRunnable runs the specified unshare command, captures its exit status,
|
||||||
|
// and exits with the same status.
|
||||||
|
func ExecRunnable(cmd Runnable) {
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
if exitError, ok := errors.Cause(err).(*exec.ExitError); ok {
|
||||||
|
if exitError.ProcessState.Exited() {
|
||||||
|
if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok {
|
||||||
|
if waitStatus.Exited() {
|
||||||
|
logrus.Errorf("%v", exitError)
|
||||||
|
os.Exit(waitStatus.ExitStatus())
|
||||||
|
}
|
||||||
|
if waitStatus.Signaled() {
|
||||||
|
logrus.Errorf("%v", exitError)
|
||||||
|
os.Exit(int(waitStatus.Signal()) + 128)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logrus.Errorf("%v", err)
|
||||||
|
logrus.Errorf("(unable to determine exit status)")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
@ -3,8 +3,8 @@
|
|||||||
package unshare
|
package unshare
|
||||||
|
|
||||||
// #cgo CFLAGS: -Wall
|
// #cgo CFLAGS: -Wall
|
||||||
// extern void _buildah_unshare(void);
|
// extern void _containers_unshare(void);
|
||||||
// void __attribute__((constructor)) init(void) {
|
// void __attribute__((constructor)) init(void) {
|
||||||
// _buildah_unshare();
|
// _containers_unshare();
|
||||||
// }
|
// }
|
||||||
import "C"
|
import "C"
|
@ -3,9 +3,9 @@
|
|||||||
package unshare
|
package unshare
|
||||||
|
|
||||||
// #cgo CFLAGS: -Wall -Wextra
|
// #cgo CFLAGS: -Wall -Wextra
|
||||||
// extern void _buildah_unshare(void);
|
// extern void _containers_unshare(void);
|
||||||
// void __attribute__((constructor)) init(void) {
|
// void __attribute__((constructor)) init(void) {
|
||||||
// _buildah_unshare();
|
// _containers_unshare();
|
||||||
// }
|
// }
|
||||||
import "C"
|
import "C"
|
||||||
|
|
@ -25,3 +25,7 @@ func GetRootlessUID() int {
|
|||||||
func RootlessEnv() []string {
|
func RootlessEnv() []string {
|
||||||
return append(os.Environ(), UsernsEnvName+"=")
|
return append(os.Environ(), UsernsEnvName+"=")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaybeReexecUsingUserNamespace re-exec the process in a new namespace
|
||||||
|
func MaybeReexecUsingUserNamespace(evenForRoot bool) {
|
||||||
|
}
|
63
vendor/github.com/containers/buildah/run.go
generated
vendored
63
vendor/github.com/containers/buildah/run.go
generated
vendored
@ -22,13 +22,15 @@ import (
|
|||||||
"github.com/containers/buildah/bind"
|
"github.com/containers/buildah/bind"
|
||||||
"github.com/containers/buildah/chroot"
|
"github.com/containers/buildah/chroot"
|
||||||
"github.com/containers/buildah/pkg/secrets"
|
"github.com/containers/buildah/pkg/secrets"
|
||||||
"github.com/containers/buildah/unshare"
|
"github.com/containers/buildah/pkg/unshare"
|
||||||
"github.com/containers/buildah/util"
|
"github.com/containers/buildah/util"
|
||||||
"github.com/containers/storage/pkg/idtools"
|
"github.com/containers/storage/pkg/idtools"
|
||||||
"github.com/containers/storage/pkg/ioutils"
|
"github.com/containers/storage/pkg/ioutils"
|
||||||
"github.com/containers/storage/pkg/reexec"
|
"github.com/containers/storage/pkg/reexec"
|
||||||
"github.com/containers/storage/pkg/stringid"
|
"github.com/containers/storage/pkg/stringid"
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
|
"github.com/docker/libnetwork/resolvconf"
|
||||||
|
"github.com/docker/libnetwork/types"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/opencontainers/runtime-tools/generate"
|
"github.com/opencontainers/runtime-tools/generate"
|
||||||
@ -593,13 +595,51 @@ func runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addNetworkConfig copies files from host and sets them up to bind mount into container
|
// addNetworkConfig copies files from host and sets them up to bind mount into container
|
||||||
func (b *Builder) addNetworkConfig(rdir, hostPath string, chownOpts *idtools.IDPair) (string, error) {
|
func (b *Builder) addNetworkConfig(rdir, hostPath string, chownOpts *idtools.IDPair, dnsServers, dnsSearch, dnsOptions []string) (string, error) {
|
||||||
copyFileWithTar := b.copyFileWithTar(chownOpts, nil)
|
stat, err := os.Stat(hostPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "error statting %q for container %q", hostPath, b.ContainerID)
|
||||||
|
}
|
||||||
|
contents, err := ioutil.ReadFile(hostPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "unable to read %s", hostPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
search := resolvconf.GetSearchDomains(contents)
|
||||||
|
nameservers := resolvconf.GetNameservers(contents, types.IP)
|
||||||
|
options := resolvconf.GetOptions(contents)
|
||||||
|
|
||||||
|
if len(dnsSearch) > 0 {
|
||||||
|
search = dnsSearch
|
||||||
|
}
|
||||||
|
if len(dnsServers) != 0 {
|
||||||
|
dns, err := getDNSIP(dnsServers)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "error getting dns servers")
|
||||||
|
}
|
||||||
|
nameservers = []string{}
|
||||||
|
for _, server := range dns {
|
||||||
|
nameservers = append(nameservers, server.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dnsOptions) != 0 {
|
||||||
|
options = dnsOptions
|
||||||
|
}
|
||||||
|
|
||||||
cfile := filepath.Join(rdir, filepath.Base(hostPath))
|
cfile := filepath.Join(rdir, filepath.Base(hostPath))
|
||||||
|
if _, err = resolvconf.Build(cfile, nameservers, search, options); err != nil {
|
||||||
|
return "", errors.Wrapf(err, "error building resolv.conf for container %s", b.ContainerID)
|
||||||
|
}
|
||||||
|
|
||||||
if err := copyFileWithTar(hostPath, cfile); err != nil {
|
uid := int(stat.Sys().(*syscall.Stat_t).Uid)
|
||||||
return "", errors.Wrapf(err, "error copying %q for container %q", cfile, b.ContainerID)
|
gid := int(stat.Sys().(*syscall.Stat_t).Gid)
|
||||||
|
if chownOpts != nil {
|
||||||
|
uid = chownOpts.UID
|
||||||
|
gid = chownOpts.GID
|
||||||
|
}
|
||||||
|
if err = os.Chown(cfile, uid, gid); err != nil {
|
||||||
|
return "", errors.Wrapf(err, "error chowning file %q for container %q", cfile, b.ContainerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := label.Relabel(cfile, b.MountLabel, false); err != nil {
|
if err := label.Relabel(cfile, b.MountLabel, false); err != nil {
|
||||||
@ -609,6 +649,17 @@ func (b *Builder) addNetworkConfig(rdir, hostPath string, chownOpts *idtools.IDP
|
|||||||
return cfile, nil
|
return cfile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDNSIP(dnsServers []string) (dns []net.IP, err error) {
|
||||||
|
for _, i := range dnsServers {
|
||||||
|
result := net.ParseIP(i)
|
||||||
|
if result == nil {
|
||||||
|
return dns, errors.Errorf("invalid IP address %s", i)
|
||||||
|
}
|
||||||
|
dns = append(dns, result)
|
||||||
|
}
|
||||||
|
return dns, nil
|
||||||
|
}
|
||||||
|
|
||||||
// generateHosts creates a containers hosts file
|
// generateHosts creates a containers hosts file
|
||||||
func (b *Builder) generateHosts(rdir, hostname string, addHosts []string, chownOpts *idtools.IDPair) (string, error) {
|
func (b *Builder) generateHosts(rdir, hostname string, addHosts []string, chownOpts *idtools.IDPair) (string, error) {
|
||||||
hostPath := "/etc/hosts"
|
hostPath := "/etc/hosts"
|
||||||
@ -1113,7 +1164,7 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !contains(volumes, "/etc/resolv.conf") {
|
if !contains(volumes, "/etc/resolv.conf") {
|
||||||
resolvFile, err := b.addNetworkConfig(path, "/etc/resolv.conf", rootIDPair)
|
resolvFile, err := b.addNetworkConfig(path, "/etc/resolv.conf", rootIDPair, b.CommonBuildOpts.DNSServers, b.CommonBuildOpts.DNSSearch, b.CommonBuildOpts.DNSOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
3
vendor/github.com/containers/buildah/vendor.conf
generated
vendored
3
vendor/github.com/containers/buildah/vendor.conf
generated
vendored
@ -8,7 +8,7 @@ github.com/vbauerster/mpb v3.3.4
|
|||||||
github.com/mattn/go-isatty v0.0.4
|
github.com/mattn/go-isatty v0.0.4
|
||||||
github.com/VividCortex/ewma v1.1.1
|
github.com/VividCortex/ewma v1.1.1
|
||||||
github.com/boltdb/bolt v1.3.1
|
github.com/boltdb/bolt v1.3.1
|
||||||
github.com/containers/storage v1.12.1
|
github.com/containers/storage v1.12.2
|
||||||
github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716
|
github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716
|
||||||
github.com/docker/docker 54dddadc7d5d89fe0be88f76979f6f6ab0dede83
|
github.com/docker/docker 54dddadc7d5d89fe0be88f76979f6f6ab0dede83
|
||||||
github.com/docker/docker-credential-helpers v0.6.1
|
github.com/docker/docker-credential-helpers v0.6.1
|
||||||
@ -65,3 +65,4 @@ github.com/klauspost/cpuid v1.2.0
|
|||||||
github.com/onsi/gomega v1.4.3
|
github.com/onsi/gomega v1.4.3
|
||||||
github.com/spf13/cobra v0.0.3
|
github.com/spf13/cobra v0.0.3
|
||||||
github.com/spf13/pflag v1.0.3
|
github.com/spf13/pflag v1.0.3
|
||||||
|
github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb
|
||||||
|
1
vendor/github.com/docker/libnetwork/resolvconf/README.md
generated
vendored
Normal file
1
vendor/github.com/docker/libnetwork/resolvconf/README.md
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf
|
26
vendor/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go
generated
vendored
Normal file
26
vendor/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
|
||||||
|
const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)`
|
||||||
|
|
||||||
|
// IPv4Localhost is a regex pattern for IPv4 localhost address range.
|
||||||
|
const IPv4Localhost = `(127\.([0-9]{1,3}\.){2}[0-9]{1,3})`
|
||||||
|
|
||||||
|
var localhostIPRegexp = regexp.MustCompile(IPLocalhost)
|
||||||
|
var localhostIPv4Regexp = regexp.MustCompile(IPv4Localhost)
|
||||||
|
|
||||||
|
// IsLocalhost returns true if ip matches the localhost IP regular expression.
|
||||||
|
// Used for determining if nameserver settings are being passed which are
|
||||||
|
// localhost addresses
|
||||||
|
func IsLocalhost(ip string) bool {
|
||||||
|
return localhostIPRegexp.MatchString(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIPv4Localhost returns true if ip matches the IPv4 localhost regular expression.
|
||||||
|
func IsIPv4Localhost(ip string) bool {
|
||||||
|
return localhostIPv4Regexp.MatchString(ip)
|
||||||
|
}
|
251
vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go
generated
vendored
Normal file
251
vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go
generated
vendored
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
// Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf
|
||||||
|
package resolvconf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
"github.com/docker/libnetwork/resolvconf/dns"
|
||||||
|
"github.com/docker/libnetwork/types"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultResolvConf points to the default file used for dns configuration on a linux machine
|
||||||
|
DefaultResolvConf = "/etc/resolv.conf"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS
|
||||||
|
defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"}
|
||||||
|
defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"}
|
||||||
|
ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)`
|
||||||
|
ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock
|
||||||
|
// This is not an IPv6 address verifier as it will accept a super-set of IPv6, and also
|
||||||
|
// will *not match* IPv4-Embedded IPv6 Addresses (RFC6052), but that and other variants
|
||||||
|
// -- e.g. other link-local types -- either won't work in containers or are unnecessary.
|
||||||
|
// For readability and sufficiency for Docker purposes this seemed more reasonable than a
|
||||||
|
// 1000+ character regexp with exact and complete IPv6 validation
|
||||||
|
ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})(%\w+)?`
|
||||||
|
|
||||||
|
localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IPLocalhost + `\s*\n*`)
|
||||||
|
nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`)
|
||||||
|
nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`)
|
||||||
|
nsIPv6Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv6Address + `))\s*$`)
|
||||||
|
nsIPv4Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `))\s*$`)
|
||||||
|
searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`)
|
||||||
|
optionsRegexp = regexp.MustCompile(`^\s*options\s*(([^\s]+\s*)*)$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
var lastModified struct {
|
||||||
|
sync.Mutex
|
||||||
|
sha256 string
|
||||||
|
contents []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// File contains the resolv.conf content and its hash
|
||||||
|
type File struct {
|
||||||
|
Content []byte
|
||||||
|
Hash string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the contents of /etc/resolv.conf and its hash
|
||||||
|
func Get() (*File, error) {
|
||||||
|
return GetSpecific(DefaultResolvConf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSpecific returns the contents of the user specified resolv.conf file and its hash
|
||||||
|
func GetSpecific(path string) (*File, error) {
|
||||||
|
resolv, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hash, err := ioutils.HashData(bytes.NewReader(resolv))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &File{Content: resolv, Hash: hash}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIfChanged retrieves the host /etc/resolv.conf file, checks against the last hash
|
||||||
|
// and, if modified since last check, returns the bytes and new hash.
|
||||||
|
// This feature is used by the resolv.conf updater for containers
|
||||||
|
func GetIfChanged() (*File, error) {
|
||||||
|
lastModified.Lock()
|
||||||
|
defer lastModified.Unlock()
|
||||||
|
|
||||||
|
resolv, err := ioutil.ReadFile("/etc/resolv.conf")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newHash, err := ioutils.HashData(bytes.NewReader(resolv))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if lastModified.sha256 != newHash {
|
||||||
|
lastModified.sha256 = newHash
|
||||||
|
lastModified.contents = resolv
|
||||||
|
return &File{Content: resolv, Hash: newHash}, nil
|
||||||
|
}
|
||||||
|
// nothing changed, so return no data
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastModified retrieves the last used contents and hash of the host resolv.conf.
|
||||||
|
// Used by containers updating on restart
|
||||||
|
func GetLastModified() *File {
|
||||||
|
lastModified.Lock()
|
||||||
|
defer lastModified.Unlock()
|
||||||
|
|
||||||
|
return &File{Content: lastModified.contents, Hash: lastModified.sha256}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterResolvDNS cleans up the config in resolvConf. It has two main jobs:
|
||||||
|
// 1. It looks for localhost (127.*|::1) entries in the provided
|
||||||
|
// resolv.conf, removing local nameserver entries, and, if the resulting
|
||||||
|
// cleaned config has no defined nameservers left, adds default DNS entries
|
||||||
|
// 2. Given the caller provides the enable/disable state of IPv6, the filter
|
||||||
|
// code will remove all IPv6 nameservers if it is not enabled for containers
|
||||||
|
//
|
||||||
|
func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
|
||||||
|
cleanedResolvConf := localhostNSRegexp.ReplaceAll(resolvConf, []byte{})
|
||||||
|
// if IPv6 is not enabled, also clean out any IPv6 address nameserver
|
||||||
|
if !ipv6Enabled {
|
||||||
|
cleanedResolvConf = nsIPv6Regexp.ReplaceAll(cleanedResolvConf, []byte{})
|
||||||
|
}
|
||||||
|
// if the resulting resolvConf has no more nameservers defined, add appropriate
|
||||||
|
// default DNS servers for IPv4 and (optionally) IPv6
|
||||||
|
if len(GetNameservers(cleanedResolvConf, types.IP)) == 0 {
|
||||||
|
logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers: %v", defaultIPv4Dns)
|
||||||
|
dns := defaultIPv4Dns
|
||||||
|
if ipv6Enabled {
|
||||||
|
logrus.Infof("IPv6 enabled; Adding default IPv6 external servers: %v", defaultIPv6Dns)
|
||||||
|
dns = append(dns, defaultIPv6Dns...)
|
||||||
|
}
|
||||||
|
cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...)
|
||||||
|
}
|
||||||
|
hash, err := ioutils.HashData(bytes.NewReader(cleanedResolvConf))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &File{Content: cleanedResolvConf, Hash: hash}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLines parses input into lines and strips away comments.
|
||||||
|
func getLines(input []byte, commentMarker []byte) [][]byte {
|
||||||
|
lines := bytes.Split(input, []byte("\n"))
|
||||||
|
var output [][]byte
|
||||||
|
for _, currentLine := range lines {
|
||||||
|
var commentIndex = bytes.Index(currentLine, commentMarker)
|
||||||
|
if commentIndex == -1 {
|
||||||
|
output = append(output, currentLine)
|
||||||
|
} else {
|
||||||
|
output = append(output, currentLine[:commentIndex])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf
|
||||||
|
func GetNameservers(resolvConf []byte, kind int) []string {
|
||||||
|
nameservers := []string{}
|
||||||
|
for _, line := range getLines(resolvConf, []byte("#")) {
|
||||||
|
var ns [][]byte
|
||||||
|
if kind == types.IP {
|
||||||
|
ns = nsRegexp.FindSubmatch(line)
|
||||||
|
} else if kind == types.IPv4 {
|
||||||
|
ns = nsIPv4Regexpmatch.FindSubmatch(line)
|
||||||
|
} else if kind == types.IPv6 {
|
||||||
|
ns = nsIPv6Regexpmatch.FindSubmatch(line)
|
||||||
|
}
|
||||||
|
if len(ns) > 0 {
|
||||||
|
nameservers = append(nameservers, string(ns[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nameservers
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNameserversAsCIDR returns nameservers (if any) listed in
|
||||||
|
// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32")
|
||||||
|
// This function's output is intended for net.ParseCIDR
|
||||||
|
func GetNameserversAsCIDR(resolvConf []byte) []string {
|
||||||
|
nameservers := []string{}
|
||||||
|
for _, nameserver := range GetNameservers(resolvConf, types.IP) {
|
||||||
|
var address string
|
||||||
|
// If IPv6, strip zone if present
|
||||||
|
if strings.Contains(nameserver, ":") {
|
||||||
|
address = strings.Split(nameserver, "%")[0] + "/128"
|
||||||
|
} else {
|
||||||
|
address = nameserver + "/32"
|
||||||
|
}
|
||||||
|
nameservers = append(nameservers, address)
|
||||||
|
}
|
||||||
|
return nameservers
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf
|
||||||
|
// If more than one search line is encountered, only the contents of the last
|
||||||
|
// one is returned.
|
||||||
|
func GetSearchDomains(resolvConf []byte) []string {
|
||||||
|
domains := []string{}
|
||||||
|
for _, line := range getLines(resolvConf, []byte("#")) {
|
||||||
|
match := searchRegexp.FindSubmatch(line)
|
||||||
|
if match == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
domains = strings.Fields(string(match[1]))
|
||||||
|
}
|
||||||
|
return domains
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOptions returns options (if any) listed in /etc/resolv.conf
|
||||||
|
// If more than one options line is encountered, only the contents of the last
|
||||||
|
// one is returned.
|
||||||
|
func GetOptions(resolvConf []byte) []string {
|
||||||
|
options := []string{}
|
||||||
|
for _, line := range getLines(resolvConf, []byte("#")) {
|
||||||
|
match := optionsRegexp.FindSubmatch(line)
|
||||||
|
if match == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
options = strings.Fields(string(match[1]))
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build writes a configuration file to path containing a "nameserver" entry
|
||||||
|
// for every element in dns, a "search" entry for every element in
|
||||||
|
// dnsSearch, and an "options" entry for every element in dnsOptions.
|
||||||
|
func Build(path string, dns, dnsSearch, dnsOptions []string) (*File, error) {
|
||||||
|
content := bytes.NewBuffer(nil)
|
||||||
|
if len(dnsSearch) > 0 {
|
||||||
|
if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." {
|
||||||
|
if _, err := content.WriteString("search " + searchString + "\n"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, dns := range dns {
|
||||||
|
if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(dnsOptions) > 0 {
|
||||||
|
if optsString := strings.Join(dnsOptions, " "); strings.Trim(optsString, " ") != "" {
|
||||||
|
if _, err := content.WriteString("options " + optsString + "\n"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := ioutils.HashData(bytes.NewReader(content.Bytes()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &File{Content: content.Bytes(), Hash: hash}, ioutil.WriteFile(path, content.Bytes(), 0644)
|
||||||
|
}
|
653
vendor/github.com/docker/libnetwork/types/types.go
generated
vendored
Normal file
653
vendor/github.com/docker/libnetwork/types/types.go
generated
vendored
Normal file
@ -0,0 +1,653 @@
|
|||||||
|
// Package types contains types that are common across libnetwork project
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ishidawataru/sctp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// constants for the IP address type
|
||||||
|
const (
|
||||||
|
IP = iota // IPv4 and IPv6
|
||||||
|
IPv4
|
||||||
|
IPv6
|
||||||
|
)
|
||||||
|
|
||||||
|
// EncryptionKey is the libnetwork representation of the key distributed by the lead
|
||||||
|
// manager.
|
||||||
|
type EncryptionKey struct {
|
||||||
|
Subsystem string
|
||||||
|
Algorithm int32
|
||||||
|
Key []byte
|
||||||
|
LamportTime uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUID represents a globally unique ID of various resources like network and endpoint
|
||||||
|
type UUID string
|
||||||
|
|
||||||
|
// QosPolicy represents a quality of service policy on an endpoint
|
||||||
|
type QosPolicy struct {
|
||||||
|
MaxEgressBandwidth uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransportPort represents a local Layer 4 endpoint
|
||||||
|
type TransportPort struct {
|
||||||
|
Proto Protocol
|
||||||
|
Port uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal checks if this instance of Transportport is equal to the passed one
|
||||||
|
func (t *TransportPort) Equal(o *TransportPort) bool {
|
||||||
|
if t == o {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if o == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Proto != o.Proto || t.Port != o.Port {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCopy returns a copy of this TransportPort structure instance
|
||||||
|
func (t *TransportPort) GetCopy() TransportPort {
|
||||||
|
return TransportPort{Proto: t.Proto, Port: t.Port}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the TransportPort structure in string form
|
||||||
|
func (t *TransportPort) String() string {
|
||||||
|
return fmt.Sprintf("%s/%d", t.Proto.String(), t.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromString reads the TransportPort structure from string
|
||||||
|
func (t *TransportPort) FromString(s string) error {
|
||||||
|
ps := strings.Split(s, "/")
|
||||||
|
if len(ps) == 2 {
|
||||||
|
t.Proto = ParseProtocol(ps[0])
|
||||||
|
if p, err := strconv.ParseUint(ps[1], 10, 16); err == nil {
|
||||||
|
t.Port = uint16(p)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BadRequestErrorf("invalid format for transport port: %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortBinding represents a port binding between the container and the host
|
||||||
|
type PortBinding struct {
|
||||||
|
Proto Protocol
|
||||||
|
IP net.IP
|
||||||
|
Port uint16
|
||||||
|
HostIP net.IP
|
||||||
|
HostPort uint16
|
||||||
|
HostPortEnd uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostAddr returns the host side transport address
|
||||||
|
func (p PortBinding) HostAddr() (net.Addr, error) {
|
||||||
|
switch p.Proto {
|
||||||
|
case UDP:
|
||||||
|
return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
|
||||||
|
case TCP:
|
||||||
|
return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
|
||||||
|
case SCTP:
|
||||||
|
return &sctp.SCTPAddr{IP: []net.IP{p.HostIP}, Port: int(p.HostPort)}, nil
|
||||||
|
default:
|
||||||
|
return nil, ErrInvalidProtocolBinding(p.Proto.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerAddr returns the container side transport address
|
||||||
|
func (p PortBinding) ContainerAddr() (net.Addr, error) {
|
||||||
|
switch p.Proto {
|
||||||
|
case UDP:
|
||||||
|
return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil
|
||||||
|
case TCP:
|
||||||
|
return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
|
||||||
|
case SCTP:
|
||||||
|
return &sctp.SCTPAddr{IP: []net.IP{p.IP}, Port: int(p.Port)}, nil
|
||||||
|
default:
|
||||||
|
return nil, ErrInvalidProtocolBinding(p.Proto.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCopy returns a copy of this PortBinding structure instance
|
||||||
|
func (p *PortBinding) GetCopy() PortBinding {
|
||||||
|
return PortBinding{
|
||||||
|
Proto: p.Proto,
|
||||||
|
IP: GetIPCopy(p.IP),
|
||||||
|
Port: p.Port,
|
||||||
|
HostIP: GetIPCopy(p.HostIP),
|
||||||
|
HostPort: p.HostPort,
|
||||||
|
HostPortEnd: p.HostPortEnd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the PortBinding structure in string form
|
||||||
|
func (p *PortBinding) String() string {
|
||||||
|
ret := fmt.Sprintf("%s/", p.Proto)
|
||||||
|
if p.IP != nil {
|
||||||
|
ret += p.IP.String()
|
||||||
|
}
|
||||||
|
ret = fmt.Sprintf("%s:%d/", ret, p.Port)
|
||||||
|
if p.HostIP != nil {
|
||||||
|
ret += p.HostIP.String()
|
||||||
|
}
|
||||||
|
ret = fmt.Sprintf("%s:%d", ret, p.HostPort)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromString reads the PortBinding structure from string s.
|
||||||
|
// String s is a triple of "protocol/containerIP:port/hostIP:port"
|
||||||
|
// containerIP and hostIP can be in dotted decimal ("192.0.2.1") or IPv6 ("2001:db8::68") form.
|
||||||
|
// Zoned addresses ("169.254.0.23%eth0" or "fe80::1ff:fe23:4567:890a%eth0") are not supported.
|
||||||
|
// If string s is incorrectly formatted or the IP addresses or ports cannot be parsed, FromString
|
||||||
|
// returns an error.
|
||||||
|
func (p *PortBinding) FromString(s string) error {
|
||||||
|
ps := strings.Split(s, "/")
|
||||||
|
if len(ps) != 3 {
|
||||||
|
return BadRequestErrorf("invalid format for port binding: %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Proto = ParseProtocol(ps[0])
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if p.IP, p.Port, err = parseIPPort(ps[1]); err != nil {
|
||||||
|
return BadRequestErrorf("failed to parse Container IP/Port in port binding: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.HostIP, p.HostPort, err = parseIPPort(ps[2]); err != nil {
|
||||||
|
return BadRequestErrorf("failed to parse Host IP/Port in port binding: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseIPPort(s string) (net.IP, uint16, error) {
|
||||||
|
hoststr, portstr, err := net.SplitHostPort(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP(hoststr)
|
||||||
|
if ip == nil {
|
||||||
|
return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr)
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := strconv.ParseUint(portstr, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, BadRequestErrorf("invalid port: %s", portstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, uint16(port), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal checks if this instance of PortBinding is equal to the passed one
|
||||||
|
func (p *PortBinding) Equal(o *PortBinding) bool {
|
||||||
|
if p == o {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if o == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Proto != o.Proto || p.Port != o.Port ||
|
||||||
|
p.HostPort != o.HostPort || p.HostPortEnd != o.HostPortEnd {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.IP != nil {
|
||||||
|
if !p.IP.Equal(o.IP) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if o.IP != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.HostIP != nil {
|
||||||
|
if !p.HostIP.Equal(o.HostIP) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if o.HostIP != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid.
|
||||||
|
type ErrInvalidProtocolBinding string
|
||||||
|
|
||||||
|
func (ipb ErrInvalidProtocolBinding) Error() string {
|
||||||
|
return fmt.Sprintf("invalid transport protocol: %s", string(ipb))
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ICMP is for the ICMP ip protocol
|
||||||
|
ICMP = 1
|
||||||
|
// TCP is for the TCP ip protocol
|
||||||
|
TCP = 6
|
||||||
|
// UDP is for the UDP ip protocol
|
||||||
|
UDP = 17
|
||||||
|
// SCTP is for the SCTP ip protocol
|
||||||
|
SCTP = 132
|
||||||
|
)
|
||||||
|
|
||||||
|
// Protocol represents an IP protocol number
|
||||||
|
type Protocol uint8
|
||||||
|
|
||||||
|
func (p Protocol) String() string {
|
||||||
|
switch p {
|
||||||
|
case ICMP:
|
||||||
|
return "icmp"
|
||||||
|
case TCP:
|
||||||
|
return "tcp"
|
||||||
|
case UDP:
|
||||||
|
return "udp"
|
||||||
|
case SCTP:
|
||||||
|
return "sctp"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%d", p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseProtocol returns the respective Protocol type for the passed string
|
||||||
|
func ParseProtocol(s string) Protocol {
|
||||||
|
switch strings.ToLower(s) {
|
||||||
|
case "icmp":
|
||||||
|
return ICMP
|
||||||
|
case "udp":
|
||||||
|
return UDP
|
||||||
|
case "tcp":
|
||||||
|
return TCP
|
||||||
|
case "sctp":
|
||||||
|
return SCTP
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMacCopy returns a copy of the passed MAC address
|
||||||
|
func GetMacCopy(from net.HardwareAddr) net.HardwareAddr {
|
||||||
|
if from == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
to := make(net.HardwareAddr, len(from))
|
||||||
|
copy(to, from)
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIPCopy returns a copy of the passed IP address
|
||||||
|
func GetIPCopy(from net.IP) net.IP {
|
||||||
|
if from == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
to := make(net.IP, len(from))
|
||||||
|
copy(to, from)
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIPNetCopy returns a copy of the passed IP Network
|
||||||
|
func GetIPNetCopy(from *net.IPNet) *net.IPNet {
|
||||||
|
if from == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bm := make(net.IPMask, len(from.Mask))
|
||||||
|
copy(bm, from.Mask)
|
||||||
|
return &net.IPNet{IP: GetIPCopy(from.IP), Mask: bm}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIPNetCanonical returns the canonical form for the passed network
|
||||||
|
func GetIPNetCanonical(nw *net.IPNet) *net.IPNet {
|
||||||
|
if nw == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c := GetIPNetCopy(nw)
|
||||||
|
c.IP = c.IP.Mask(nw.Mask)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareIPNet returns equal if the two IP Networks are equal
|
||||||
|
func CompareIPNet(a, b *net.IPNet) bool {
|
||||||
|
if a == b {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if a == nil || b == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinimalIP returns the address in its shortest form
|
||||||
|
// If ip contains an IPv4-mapped IPv6 address, the 4-octet form of the IPv4 address will be returned.
|
||||||
|
// Otherwise ip is returned unchanged.
|
||||||
|
func GetMinimalIP(ip net.IP) net.IP {
|
||||||
|
if ip != nil && ip.To4() != nil {
|
||||||
|
return ip.To4()
|
||||||
|
}
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinimalIPNet returns a copy of the passed IP Network with congruent ip and mask notation
|
||||||
|
func GetMinimalIPNet(nw *net.IPNet) *net.IPNet {
|
||||||
|
if nw == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(nw.IP) == 16 && nw.IP.To4() != nil {
|
||||||
|
m := nw.Mask
|
||||||
|
if len(m) == 16 {
|
||||||
|
m = m[12:16]
|
||||||
|
}
|
||||||
|
return &net.IPNet{IP: nw.IP.To4(), Mask: m}
|
||||||
|
}
|
||||||
|
return nw
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIPNetValid returns true if the ipnet is a valid network/mask
|
||||||
|
// combination. Otherwise returns false.
|
||||||
|
func IsIPNetValid(nw *net.IPNet) bool {
|
||||||
|
return nw.String() != "0.0.0.0/0"
|
||||||
|
}
|
||||||
|
|
||||||
|
var v4inV6MaskPrefix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||||
|
|
||||||
|
// compareIPMask checks if the passed ip and mask are semantically compatible.
|
||||||
|
// It returns the byte indexes for the address and mask so that caller can
|
||||||
|
// do bitwise operations without modifying address representation.
|
||||||
|
func compareIPMask(ip net.IP, mask net.IPMask) (is int, ms int, err error) {
|
||||||
|
// Find the effective starting of address and mask
|
||||||
|
if len(ip) == net.IPv6len && ip.To4() != nil {
|
||||||
|
is = 12
|
||||||
|
}
|
||||||
|
if len(ip[is:]) == net.IPv4len && len(mask) == net.IPv6len && bytes.Equal(mask[:12], v4inV6MaskPrefix) {
|
||||||
|
ms = 12
|
||||||
|
}
|
||||||
|
// Check if address and mask are semantically compatible
|
||||||
|
if len(ip[is:]) != len(mask[ms:]) {
|
||||||
|
err = fmt.Errorf("ip and mask are not compatible: (%#v, %#v)", ip, mask)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHostPartIP returns the host portion of the ip address identified by the mask.
|
||||||
|
// IP address representation is not modified. If address and mask are not compatible
|
||||||
|
// an error is returned.
|
||||||
|
func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) {
|
||||||
|
// Find the effective starting of address and mask
|
||||||
|
is, ms, err := compareIPMask(ip, mask)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot compute host portion ip address because %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute host portion
|
||||||
|
out := GetIPCopy(ip)
|
||||||
|
for i := 0; i < len(mask[ms:]); i++ {
|
||||||
|
out[is+i] &= ^mask[ms+i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBroadcastIP returns the broadcast ip address for the passed network (ip and mask).
|
||||||
|
// IP address representation is not modified. If address and mask are not compatible
|
||||||
|
// an error is returned.
|
||||||
|
func GetBroadcastIP(ip net.IP, mask net.IPMask) (net.IP, error) {
|
||||||
|
// Find the effective starting of address and mask
|
||||||
|
is, ms, err := compareIPMask(ip, mask)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot compute broadcast ip address because %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute broadcast address
|
||||||
|
out := GetIPCopy(ip)
|
||||||
|
for i := 0; i < len(mask[ms:]); i++ {
|
||||||
|
out[is+i] |= ^mask[ms+i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseCIDR returns the *net.IPNet represented by the passed CIDR notation
|
||||||
|
func ParseCIDR(cidr string) (n *net.IPNet, e error) {
|
||||||
|
var i net.IP
|
||||||
|
if i, n, e = net.ParseCIDR(cidr); e == nil {
|
||||||
|
n.IP = i
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NEXTHOP indicates a StaticRoute with an IP next hop.
|
||||||
|
NEXTHOP = iota
|
||||||
|
|
||||||
|
// CONNECTED indicates a StaticRoute with an interface for directly connected peers.
|
||||||
|
CONNECTED
|
||||||
|
)
|
||||||
|
|
||||||
|
// StaticRoute is a statically-provisioned IP route.
|
||||||
|
type StaticRoute struct {
|
||||||
|
Destination *net.IPNet
|
||||||
|
|
||||||
|
RouteType int // NEXT_HOP or CONNECTED
|
||||||
|
|
||||||
|
// NextHop will be resolved by the kernel (i.e. as a loose hop).
|
||||||
|
NextHop net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCopy returns a copy of this StaticRoute structure
|
||||||
|
func (r *StaticRoute) GetCopy() *StaticRoute {
|
||||||
|
d := GetIPNetCopy(r.Destination)
|
||||||
|
nh := GetIPCopy(r.NextHop)
|
||||||
|
return &StaticRoute{Destination: d,
|
||||||
|
RouteType: r.RouteType,
|
||||||
|
NextHop: nh,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceStatistics represents the interface's statistics
|
||||||
|
type InterfaceStatistics struct {
|
||||||
|
RxBytes uint64
|
||||||
|
RxPackets uint64
|
||||||
|
RxErrors uint64
|
||||||
|
RxDropped uint64
|
||||||
|
TxBytes uint64
|
||||||
|
TxPackets uint64
|
||||||
|
TxErrors uint64
|
||||||
|
TxDropped uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InterfaceStatistics) String() string {
|
||||||
|
return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d",
|
||||||
|
is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped)
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
* Well-known Error Interfaces
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
// MaskableError is an interface for errors which can be ignored by caller
|
||||||
|
type MaskableError interface {
|
||||||
|
// Maskable makes implementer into MaskableError type
|
||||||
|
Maskable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryError is an interface for errors which might get resolved through retry
|
||||||
|
type RetryError interface {
|
||||||
|
// Retry makes implementer into RetryError type
|
||||||
|
Retry()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BadRequestError is an interface for errors originated by a bad request
|
||||||
|
type BadRequestError interface {
|
||||||
|
// BadRequest makes implementer into BadRequestError type
|
||||||
|
BadRequest()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFoundError is an interface for errors raised because a needed resource is not available
|
||||||
|
type NotFoundError interface {
|
||||||
|
// NotFound makes implementer into NotFoundError type
|
||||||
|
NotFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForbiddenError is an interface for errors which denote a valid request that cannot be honored
|
||||||
|
type ForbiddenError interface {
|
||||||
|
// Forbidden makes implementer into ForbiddenError type
|
||||||
|
Forbidden()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoServiceError is an interface for errors returned when the required service is not available
|
||||||
|
type NoServiceError interface {
|
||||||
|
// NoService makes implementer into NoServiceError type
|
||||||
|
NoService()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeoutError is an interface for errors raised because of timeout
|
||||||
|
type TimeoutError interface {
|
||||||
|
// Timeout makes implementer into TimeoutError type
|
||||||
|
Timeout()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotImplementedError is an interface for errors raised because of requested functionality is not yet implemented
|
||||||
|
type NotImplementedError interface {
|
||||||
|
// NotImplemented makes implementer into NotImplementedError type
|
||||||
|
NotImplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalError is an interface for errors raised because of an internal error
|
||||||
|
type InternalError interface {
|
||||||
|
// Internal makes implementer into InternalError type
|
||||||
|
Internal()
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
* Well-known Error Formatters
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
// BadRequestErrorf creates an instance of BadRequestError
|
||||||
|
func BadRequestErrorf(format string, params ...interface{}) error {
|
||||||
|
return badRequest(fmt.Sprintf(format, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFoundErrorf creates an instance of NotFoundError
|
||||||
|
func NotFoundErrorf(format string, params ...interface{}) error {
|
||||||
|
return notFound(fmt.Sprintf(format, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForbiddenErrorf creates an instance of ForbiddenError
|
||||||
|
func ForbiddenErrorf(format string, params ...interface{}) error {
|
||||||
|
return forbidden(fmt.Sprintf(format, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoServiceErrorf creates an instance of NoServiceError
|
||||||
|
func NoServiceErrorf(format string, params ...interface{}) error {
|
||||||
|
return noService(fmt.Sprintf(format, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotImplementedErrorf creates an instance of NotImplementedError
|
||||||
|
func NotImplementedErrorf(format string, params ...interface{}) error {
|
||||||
|
return notImpl(fmt.Sprintf(format, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeoutErrorf creates an instance of TimeoutError
|
||||||
|
func TimeoutErrorf(format string, params ...interface{}) error {
|
||||||
|
return timeout(fmt.Sprintf(format, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalErrorf creates an instance of InternalError
|
||||||
|
func InternalErrorf(format string, params ...interface{}) error {
|
||||||
|
return internal(fmt.Sprintf(format, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalMaskableErrorf creates an instance of InternalError and MaskableError
|
||||||
|
func InternalMaskableErrorf(format string, params ...interface{}) error {
|
||||||
|
return maskInternal(fmt.Sprintf(format, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryErrorf creates an instance of RetryError
|
||||||
|
func RetryErrorf(format string, params ...interface{}) error {
|
||||||
|
return retry(fmt.Sprintf(format, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************
|
||||||
|
* Internal Error Types
|
||||||
|
***********************/
|
||||||
|
type badRequest string
|
||||||
|
|
||||||
|
func (br badRequest) Error() string {
|
||||||
|
return string(br)
|
||||||
|
}
|
||||||
|
func (br badRequest) BadRequest() {}
|
||||||
|
|
||||||
|
type maskBadRequest string
|
||||||
|
|
||||||
|
type notFound string
|
||||||
|
|
||||||
|
func (nf notFound) Error() string {
|
||||||
|
return string(nf)
|
||||||
|
}
|
||||||
|
func (nf notFound) NotFound() {}
|
||||||
|
|
||||||
|
type forbidden string
|
||||||
|
|
||||||
|
func (frb forbidden) Error() string {
|
||||||
|
return string(frb)
|
||||||
|
}
|
||||||
|
func (frb forbidden) Forbidden() {}
|
||||||
|
|
||||||
|
type noService string
|
||||||
|
|
||||||
|
func (ns noService) Error() string {
|
||||||
|
return string(ns)
|
||||||
|
}
|
||||||
|
func (ns noService) NoService() {}
|
||||||
|
|
||||||
|
type maskNoService string
|
||||||
|
|
||||||
|
type timeout string
|
||||||
|
|
||||||
|
func (to timeout) Error() string {
|
||||||
|
return string(to)
|
||||||
|
}
|
||||||
|
func (to timeout) Timeout() {}
|
||||||
|
|
||||||
|
type notImpl string
|
||||||
|
|
||||||
|
func (ni notImpl) Error() string {
|
||||||
|
return string(ni)
|
||||||
|
}
|
||||||
|
func (ni notImpl) NotImplemented() {}
|
||||||
|
|
||||||
|
type internal string
|
||||||
|
|
||||||
|
func (nt internal) Error() string {
|
||||||
|
return string(nt)
|
||||||
|
}
|
||||||
|
func (nt internal) Internal() {}
|
||||||
|
|
||||||
|
type maskInternal string
|
||||||
|
|
||||||
|
func (mnt maskInternal) Error() string {
|
||||||
|
return string(mnt)
|
||||||
|
}
|
||||||
|
func (mnt maskInternal) Internal() {}
|
||||||
|
func (mnt maskInternal) Maskable() {}
|
||||||
|
|
||||||
|
type retry string
|
||||||
|
|
||||||
|
func (r retry) Error() string {
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
func (r retry) Retry() {}
|
201
vendor/github.com/ishidawataru/sctp/LICENSE
generated
vendored
Normal file
201
vendor/github.com/ishidawataru/sctp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed 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.
|
18
vendor/github.com/ishidawataru/sctp/README.md
generated
vendored
Normal file
18
vendor/github.com/ishidawataru/sctp/README.md
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Stream Control Transmission Protocol (SCTP)
|
||||||
|
----
|
||||||
|
|
||||||
|
[](https://travis-ci.org/ishidawataru/sctp/builds)
|
||||||
|
|
||||||
|
Examples
|
||||||
|
----
|
||||||
|
|
||||||
|
See `example/sctp.go`
|
||||||
|
|
||||||
|
```go
|
||||||
|
$ cd example
|
||||||
|
$ go build
|
||||||
|
$ # run example SCTP server
|
||||||
|
$ ./example -server -port 1000 -ip 10.10.0.1,10.20.0.1
|
||||||
|
$ # run example SCTP client
|
||||||
|
$ ./example -port 1000 -ip 10.10.0.1,10.20.0.1
|
||||||
|
```
|
656
vendor/github.com/ishidawataru/sctp/sctp.go
generated
vendored
Normal file
656
vendor/github.com/ishidawataru/sctp/sctp.go
generated
vendored
Normal file
@ -0,0 +1,656 @@
|
|||||||
|
package sctp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SOL_SCTP = 132
|
||||||
|
|
||||||
|
SCTP_BINDX_ADD_ADDR = 0x01
|
||||||
|
SCTP_BINDX_REM_ADDR = 0x02
|
||||||
|
|
||||||
|
MSG_NOTIFICATION = 0x8000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SCTP_RTOINFO = iota
|
||||||
|
SCTP_ASSOCINFO
|
||||||
|
SCTP_INITMSG
|
||||||
|
SCTP_NODELAY
|
||||||
|
SCTP_AUTOCLOSE
|
||||||
|
SCTP_SET_PEER_PRIMARY_ADDR
|
||||||
|
SCTP_PRIMARY_ADDR
|
||||||
|
SCTP_ADAPTATION_LAYER
|
||||||
|
SCTP_DISABLE_FRAGMENTS
|
||||||
|
SCTP_PEER_ADDR_PARAMS
|
||||||
|
SCTP_DEFAULT_SENT_PARAM
|
||||||
|
SCTP_EVENTS
|
||||||
|
SCTP_I_WANT_MAPPED_V4_ADDR
|
||||||
|
SCTP_MAXSEG
|
||||||
|
SCTP_STATUS
|
||||||
|
SCTP_GET_PEER_ADDR_INFO
|
||||||
|
SCTP_DELAYED_ACK_TIME
|
||||||
|
SCTP_DELAYED_ACK = SCTP_DELAYED_ACK_TIME
|
||||||
|
SCTP_DELAYED_SACK = SCTP_DELAYED_ACK_TIME
|
||||||
|
|
||||||
|
SCTP_SOCKOPT_BINDX_ADD = 100
|
||||||
|
SCTP_SOCKOPT_BINDX_REM = 101
|
||||||
|
SCTP_SOCKOPT_PEELOFF = 102
|
||||||
|
SCTP_GET_PEER_ADDRS = 108
|
||||||
|
SCTP_GET_LOCAL_ADDRS = 109
|
||||||
|
SCTP_SOCKOPT_CONNECTX = 110
|
||||||
|
SCTP_SOCKOPT_CONNECTX3 = 111
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SCTP_EVENT_DATA_IO = 1 << iota
|
||||||
|
SCTP_EVENT_ASSOCIATION
|
||||||
|
SCTP_EVENT_ADDRESS
|
||||||
|
SCTP_EVENT_SEND_FAILURE
|
||||||
|
SCTP_EVENT_PEER_ERROR
|
||||||
|
SCTP_EVENT_SHUTDOWN
|
||||||
|
SCTP_EVENT_PARTIAL_DELIVERY
|
||||||
|
SCTP_EVENT_ADAPTATION_LAYER
|
||||||
|
SCTP_EVENT_AUTHENTICATION
|
||||||
|
SCTP_EVENT_SENDER_DRY
|
||||||
|
|
||||||
|
SCTP_EVENT_ALL = SCTP_EVENT_DATA_IO | SCTP_EVENT_ASSOCIATION | SCTP_EVENT_ADDRESS | SCTP_EVENT_SEND_FAILURE | SCTP_EVENT_PEER_ERROR | SCTP_EVENT_SHUTDOWN | SCTP_EVENT_PARTIAL_DELIVERY | SCTP_EVENT_ADAPTATION_LAYER | SCTP_EVENT_AUTHENTICATION | SCTP_EVENT_SENDER_DRY
|
||||||
|
)
|
||||||
|
|
||||||
|
type SCTPNotificationType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SCTP_SN_TYPE_BASE = SCTPNotificationType(iota + (1 << 15))
|
||||||
|
SCTP_ASSOC_CHANGE
|
||||||
|
SCTP_PEER_ADDR_CHANGE
|
||||||
|
SCTP_SEND_FAILED
|
||||||
|
SCTP_REMOTE_ERROR
|
||||||
|
SCTP_SHUTDOWN_EVENT
|
||||||
|
SCTP_PARTIAL_DELIVERY_EVENT
|
||||||
|
SCTP_ADAPTATION_INDICATION
|
||||||
|
SCTP_AUTHENTICATION_INDICATION
|
||||||
|
SCTP_SENDER_DRY_EVENT
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotificationHandler func([]byte) error
|
||||||
|
|
||||||
|
type EventSubscribe struct {
|
||||||
|
DataIO uint8
|
||||||
|
Association uint8
|
||||||
|
Address uint8
|
||||||
|
SendFailure uint8
|
||||||
|
PeerError uint8
|
||||||
|
Shutdown uint8
|
||||||
|
PartialDelivery uint8
|
||||||
|
AdaptationLayer uint8
|
||||||
|
Authentication uint8
|
||||||
|
SenderDry uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
SCTP_CMSG_INIT = iota
|
||||||
|
SCTP_CMSG_SNDRCV
|
||||||
|
SCTP_CMSG_SNDINFO
|
||||||
|
SCTP_CMSG_RCVINFO
|
||||||
|
SCTP_CMSG_NXTINFO
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SCTP_UNORDERED = 1 << iota
|
||||||
|
SCTP_ADDR_OVER
|
||||||
|
SCTP_ABORT
|
||||||
|
SCTP_SACK_IMMEDIATELY
|
||||||
|
SCTP_EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SCTP_MAX_STREAM = 0xffff
|
||||||
|
)
|
||||||
|
|
||||||
|
type InitMsg struct {
|
||||||
|
NumOstreams uint16
|
||||||
|
MaxInstreams uint16
|
||||||
|
MaxAttempts uint16
|
||||||
|
MaxInitTimeout uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type SndRcvInfo struct {
|
||||||
|
Stream uint16
|
||||||
|
SSN uint16
|
||||||
|
Flags uint16
|
||||||
|
_ uint16
|
||||||
|
PPID uint32
|
||||||
|
Context uint32
|
||||||
|
TTL uint32
|
||||||
|
TSN uint32
|
||||||
|
CumTSN uint32
|
||||||
|
AssocID int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type SndInfo struct {
|
||||||
|
SID uint16
|
||||||
|
Flags uint16
|
||||||
|
PPID uint32
|
||||||
|
Context uint32
|
||||||
|
AssocID int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetAddrsOld struct {
|
||||||
|
AssocID int32
|
||||||
|
AddrNum int32
|
||||||
|
Addrs uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotificationHeader struct {
|
||||||
|
Type uint16
|
||||||
|
Flags uint16
|
||||||
|
Length uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type SCTPState uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
SCTP_COMM_UP = SCTPState(iota)
|
||||||
|
SCTP_COMM_LOST
|
||||||
|
SCTP_RESTART
|
||||||
|
SCTP_SHUTDOWN_COMP
|
||||||
|
SCTP_CANT_STR_ASSOC
|
||||||
|
)
|
||||||
|
|
||||||
|
var nativeEndian binary.ByteOrder
|
||||||
|
var sndRcvInfoSize uintptr
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
i := uint16(1)
|
||||||
|
if *(*byte)(unsafe.Pointer(&i)) == 0 {
|
||||||
|
nativeEndian = binary.BigEndian
|
||||||
|
} else {
|
||||||
|
nativeEndian = binary.LittleEndian
|
||||||
|
}
|
||||||
|
info := SndRcvInfo{}
|
||||||
|
sndRcvInfoSize = unsafe.Sizeof(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toBuf(v interface{}) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
binary.Write(&buf, nativeEndian, v)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func htons(h uint16) uint16 {
|
||||||
|
if nativeEndian == binary.LittleEndian {
|
||||||
|
return (h << 8 & 0xff00) | (h >> 8 & 0xff)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
var ntohs = htons
|
||||||
|
|
||||||
|
func setNumOstreams(fd, num int) error {
|
||||||
|
param := InitMsg{
|
||||||
|
NumOstreams: uint16(num),
|
||||||
|
}
|
||||||
|
optlen := unsafe.Sizeof(param)
|
||||||
|
_, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(¶m)), uintptr(optlen))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type SCTPAddr struct {
|
||||||
|
IP []net.IP
|
||||||
|
Port int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *SCTPAddr) ToRawSockAddrBuf() []byte {
|
||||||
|
buf := []byte{}
|
||||||
|
p := htons(uint16(a.Port))
|
||||||
|
for _, ip := range a.IP {
|
||||||
|
if ip.To4() != nil {
|
||||||
|
s := syscall.RawSockaddrInet4{
|
||||||
|
Family: syscall.AF_INET,
|
||||||
|
Port: p,
|
||||||
|
}
|
||||||
|
copy(s.Addr[:], ip.To4())
|
||||||
|
buf = append(buf, toBuf(s)...)
|
||||||
|
} else {
|
||||||
|
s := syscall.RawSockaddrInet6{
|
||||||
|
Family: syscall.AF_INET6,
|
||||||
|
Port: p,
|
||||||
|
}
|
||||||
|
copy(s.Addr[:], ip)
|
||||||
|
buf = append(buf, toBuf(s)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *SCTPAddr) String() string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
for n, i := range a.IP {
|
||||||
|
if a.IP[n].To4() != nil {
|
||||||
|
b.WriteString(i.String())
|
||||||
|
} else if a.IP[n].To16() != nil {
|
||||||
|
b.WriteRune('[')
|
||||||
|
b.WriteString(i.String())
|
||||||
|
b.WriteRune(']')
|
||||||
|
}
|
||||||
|
if n < len(a.IP)-1 {
|
||||||
|
b.WriteRune('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.WriteRune(':')
|
||||||
|
b.WriteString(strconv.Itoa(a.Port))
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *SCTPAddr) Network() string { return "sctp" }
|
||||||
|
|
||||||
|
func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) {
|
||||||
|
tcpnet := ""
|
||||||
|
switch network {
|
||||||
|
case "", "sctp":
|
||||||
|
case "sctp4":
|
||||||
|
tcpnet = "tcp4"
|
||||||
|
case "sctp6":
|
||||||
|
tcpnet = "tcp6"
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid net: %s", network)
|
||||||
|
}
|
||||||
|
elems := strings.Split(addrs, "/")
|
||||||
|
if len(elems) == 0 {
|
||||||
|
return nil, fmt.Errorf("invalid input: %s", addrs)
|
||||||
|
}
|
||||||
|
ipaddrs := make([]net.IP, 0, len(elems))
|
||||||
|
for _, e := range elems[:len(elems)-1] {
|
||||||
|
tcpa, err := net.ResolveTCPAddr(tcpnet, e+":")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ipaddrs = append(ipaddrs, tcpa.IP)
|
||||||
|
}
|
||||||
|
tcpa, err := net.ResolveTCPAddr(tcpnet, elems[len(elems)-1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tcpa.IP != nil {
|
||||||
|
ipaddrs = append(ipaddrs, tcpa.IP)
|
||||||
|
} else {
|
||||||
|
ipaddrs = nil
|
||||||
|
}
|
||||||
|
return &SCTPAddr{
|
||||||
|
IP: ipaddrs,
|
||||||
|
Port: tcpa.Port,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SCTPConnect(fd int, addr *SCTPAddr) (int, error) {
|
||||||
|
buf := addr.ToRawSockAddrBuf()
|
||||||
|
param := GetAddrsOld{
|
||||||
|
AddrNum: int32(len(buf)),
|
||||||
|
Addrs: uintptr(uintptr(unsafe.Pointer(&buf[0]))),
|
||||||
|
}
|
||||||
|
optlen := unsafe.Sizeof(param)
|
||||||
|
_, _, err := getsockopt(fd, SCTP_SOCKOPT_CONNECTX3, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen)))
|
||||||
|
if err == nil {
|
||||||
|
return int(param.AssocID), nil
|
||||||
|
} else if err != syscall.ENOPROTOOPT {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
r0, _, err := setsockopt(fd, SCTP_SOCKOPT_CONNECTX, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
|
||||||
|
return int(r0), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SCTPBind(fd int, addr *SCTPAddr, flags int) error {
|
||||||
|
var option uintptr
|
||||||
|
switch flags {
|
||||||
|
case SCTP_BINDX_ADD_ADDR:
|
||||||
|
option = SCTP_SOCKOPT_BINDX_ADD
|
||||||
|
case SCTP_BINDX_REM_ADDR:
|
||||||
|
option = SCTP_SOCKOPT_BINDX_REM
|
||||||
|
default:
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := addr.ToRawSockAddrBuf()
|
||||||
|
_, _, err := setsockopt(fd, option, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type SCTPConn struct {
|
||||||
|
_fd int32
|
||||||
|
notificationHandler NotificationHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) fd() int {
|
||||||
|
return int(atomic.LoadInt32(&c._fd))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSCTPConn(fd int, handler NotificationHandler) *SCTPConn {
|
||||||
|
conn := &SCTPConn{
|
||||||
|
_fd: int32(fd),
|
||||||
|
notificationHandler: handler,
|
||||||
|
}
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) Write(b []byte) (int, error) {
|
||||||
|
return c.SCTPWrite(b, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) Read(b []byte) (int, error) {
|
||||||
|
n, _, err := c.SCTPRead(b)
|
||||||
|
if n < 0 {
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SetInitMsg(numOstreams, maxInstreams, maxAttempts, maxInitTimeout int) error {
|
||||||
|
param := InitMsg{
|
||||||
|
NumOstreams: uint16(numOstreams),
|
||||||
|
MaxInstreams: uint16(maxInstreams),
|
||||||
|
MaxAttempts: uint16(maxAttempts),
|
||||||
|
MaxInitTimeout: uint16(maxInitTimeout),
|
||||||
|
}
|
||||||
|
optlen := unsafe.Sizeof(param)
|
||||||
|
_, _, err := setsockopt(c.fd(), SCTP_INITMSG, uintptr(unsafe.Pointer(¶m)), uintptr(optlen))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SubscribeEvents(flags int) error {
|
||||||
|
var d, a, ad, sf, p, sh, pa, ada, au, se uint8
|
||||||
|
if flags&SCTP_EVENT_DATA_IO > 0 {
|
||||||
|
d = 1
|
||||||
|
}
|
||||||
|
if flags&SCTP_EVENT_ASSOCIATION > 0 {
|
||||||
|
a = 1
|
||||||
|
}
|
||||||
|
if flags&SCTP_EVENT_ADDRESS > 0 {
|
||||||
|
ad = 1
|
||||||
|
}
|
||||||
|
if flags&SCTP_EVENT_SEND_FAILURE > 0 {
|
||||||
|
sf = 1
|
||||||
|
}
|
||||||
|
if flags&SCTP_EVENT_PEER_ERROR > 0 {
|
||||||
|
p = 1
|
||||||
|
}
|
||||||
|
if flags&SCTP_EVENT_SHUTDOWN > 0 {
|
||||||
|
sh = 1
|
||||||
|
}
|
||||||
|
if flags&SCTP_EVENT_PARTIAL_DELIVERY > 0 {
|
||||||
|
pa = 1
|
||||||
|
}
|
||||||
|
if flags&SCTP_EVENT_ADAPTATION_LAYER > 0 {
|
||||||
|
ada = 1
|
||||||
|
}
|
||||||
|
if flags&SCTP_EVENT_AUTHENTICATION > 0 {
|
||||||
|
au = 1
|
||||||
|
}
|
||||||
|
if flags&SCTP_EVENT_SENDER_DRY > 0 {
|
||||||
|
se = 1
|
||||||
|
}
|
||||||
|
param := EventSubscribe{
|
||||||
|
DataIO: d,
|
||||||
|
Association: a,
|
||||||
|
Address: ad,
|
||||||
|
SendFailure: sf,
|
||||||
|
PeerError: p,
|
||||||
|
Shutdown: sh,
|
||||||
|
PartialDelivery: pa,
|
||||||
|
AdaptationLayer: ada,
|
||||||
|
Authentication: au,
|
||||||
|
SenderDry: se,
|
||||||
|
}
|
||||||
|
optlen := unsafe.Sizeof(param)
|
||||||
|
_, _, err := setsockopt(c.fd(), SCTP_EVENTS, uintptr(unsafe.Pointer(¶m)), uintptr(optlen))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SubscribedEvents() (int, error) {
|
||||||
|
param := EventSubscribe{}
|
||||||
|
optlen := unsafe.Sizeof(param)
|
||||||
|
_, _, err := getsockopt(c.fd(), SCTP_EVENTS, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen)))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var flags int
|
||||||
|
if param.DataIO > 0 {
|
||||||
|
flags |= SCTP_EVENT_DATA_IO
|
||||||
|
}
|
||||||
|
if param.Association > 0 {
|
||||||
|
flags |= SCTP_EVENT_ASSOCIATION
|
||||||
|
}
|
||||||
|
if param.Address > 0 {
|
||||||
|
flags |= SCTP_EVENT_ADDRESS
|
||||||
|
}
|
||||||
|
if param.SendFailure > 0 {
|
||||||
|
flags |= SCTP_EVENT_SEND_FAILURE
|
||||||
|
}
|
||||||
|
if param.PeerError > 0 {
|
||||||
|
flags |= SCTP_EVENT_PEER_ERROR
|
||||||
|
}
|
||||||
|
if param.Shutdown > 0 {
|
||||||
|
flags |= SCTP_EVENT_SHUTDOWN
|
||||||
|
}
|
||||||
|
if param.PartialDelivery > 0 {
|
||||||
|
flags |= SCTP_EVENT_PARTIAL_DELIVERY
|
||||||
|
}
|
||||||
|
if param.AdaptationLayer > 0 {
|
||||||
|
flags |= SCTP_EVENT_ADAPTATION_LAYER
|
||||||
|
}
|
||||||
|
if param.Authentication > 0 {
|
||||||
|
flags |= SCTP_EVENT_AUTHENTICATION
|
||||||
|
}
|
||||||
|
if param.SenderDry > 0 {
|
||||||
|
flags |= SCTP_EVENT_SENDER_DRY
|
||||||
|
}
|
||||||
|
return flags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SetDefaultSentParam(info *SndRcvInfo) error {
|
||||||
|
optlen := unsafe.Sizeof(*info)
|
||||||
|
_, _, err := setsockopt(c.fd(), SCTP_DEFAULT_SENT_PARAM, uintptr(unsafe.Pointer(info)), uintptr(optlen))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) GetDefaultSentParam() (*SndRcvInfo, error) {
|
||||||
|
info := &SndRcvInfo{}
|
||||||
|
optlen := unsafe.Sizeof(*info)
|
||||||
|
_, _, err := getsockopt(c.fd(), SCTP_DEFAULT_SENT_PARAM, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(&optlen)))
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) {
|
||||||
|
addr := &SCTPAddr{
|
||||||
|
IP: make([]net.IP, n),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch family := (*(*syscall.RawSockaddrAny)(ptr)).Addr.Family; family {
|
||||||
|
case syscall.AF_INET:
|
||||||
|
addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port)))
|
||||||
|
tmp := syscall.RawSockaddrInet4{}
|
||||||
|
size := unsafe.Sizeof(tmp)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
a := *(*syscall.RawSockaddrInet4)(unsafe.Pointer(
|
||||||
|
uintptr(ptr) + size*uintptr(i)))
|
||||||
|
addr.IP[i] = a.Addr[:]
|
||||||
|
}
|
||||||
|
case syscall.AF_INET6:
|
||||||
|
addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port)))
|
||||||
|
tmp := syscall.RawSockaddrInet6{}
|
||||||
|
size := unsafe.Sizeof(tmp)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
a := *(*syscall.RawSockaddrInet6)(unsafe.Pointer(
|
||||||
|
uintptr(ptr) + size*uintptr(i)))
|
||||||
|
addr.IP[i] = a.Addr[:]
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown address family: %d", family)
|
||||||
|
}
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sctpGetAddrs(fd, id, optname int) (*SCTPAddr, error) {
|
||||||
|
|
||||||
|
type getaddrs struct {
|
||||||
|
assocId int32
|
||||||
|
addrNum uint32
|
||||||
|
addrs [4096]byte
|
||||||
|
}
|
||||||
|
param := getaddrs{
|
||||||
|
assocId: int32(id),
|
||||||
|
}
|
||||||
|
optlen := unsafe.Sizeof(param)
|
||||||
|
_, _, err := getsockopt(fd, uintptr(optname), uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resolveFromRawAddr(unsafe.Pointer(¶m.addrs), int(param.addrNum))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SCTPGetPrimaryPeerAddr() (*SCTPAddr, error) {
|
||||||
|
|
||||||
|
type sctpGetSetPrim struct {
|
||||||
|
assocId int32
|
||||||
|
addrs [128]byte
|
||||||
|
}
|
||||||
|
param := sctpGetSetPrim{
|
||||||
|
assocId: int32(0),
|
||||||
|
}
|
||||||
|
optlen := unsafe.Sizeof(param)
|
||||||
|
_, _, err := getsockopt(c.fd(), SCTP_PRIMARY_ADDR, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resolveFromRawAddr(unsafe.Pointer(¶m.addrs), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SCTPLocalAddr(id int) (*SCTPAddr, error) {
|
||||||
|
return sctpGetAddrs(c.fd(), id, SCTP_GET_LOCAL_ADDRS)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SCTPRemoteAddr(id int) (*SCTPAddr, error) {
|
||||||
|
return sctpGetAddrs(c.fd(), id, SCTP_GET_PEER_ADDRS)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) LocalAddr() net.Addr {
|
||||||
|
addr, err := sctpGetAddrs(c.fd(), 0, SCTP_GET_LOCAL_ADDRS)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) RemoteAddr() net.Addr {
|
||||||
|
addr, err := sctpGetAddrs(c.fd(), 0, SCTP_GET_PEER_ADDRS)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) PeelOff(id int) (*SCTPConn, error) {
|
||||||
|
type peeloffArg struct {
|
||||||
|
assocId int32
|
||||||
|
sd int
|
||||||
|
}
|
||||||
|
param := peeloffArg{
|
||||||
|
assocId: int32(id),
|
||||||
|
}
|
||||||
|
optlen := unsafe.Sizeof(param)
|
||||||
|
_, _, err := getsockopt(c.fd(), SCTP_SOCKOPT_PEELOFF, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &SCTPConn{_fd: int32(param.sd)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SetDeadline(t time.Time) error {
|
||||||
|
return syscall.EOPNOTSUPP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return syscall.EOPNOTSUPP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return syscall.EOPNOTSUPP
|
||||||
|
}
|
||||||
|
|
||||||
|
type SCTPListener struct {
|
||||||
|
fd int
|
||||||
|
m sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *SCTPListener) Addr() net.Addr {
|
||||||
|
laddr, err := sctpGetAddrs(ln.fd, 0, SCTP_GET_LOCAL_ADDRS)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return laddr
|
||||||
|
}
|
||||||
|
|
||||||
|
type SCTPSndRcvInfoWrappedConn struct {
|
||||||
|
conn *SCTPConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSCTPSndRcvInfoWrappedConn(conn *SCTPConn) *SCTPSndRcvInfoWrappedConn {
|
||||||
|
conn.SubscribeEvents(SCTP_EVENT_DATA_IO)
|
||||||
|
return &SCTPSndRcvInfoWrappedConn{conn}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPSndRcvInfoWrappedConn) Write(b []byte) (int, error) {
|
||||||
|
if len(b) < int(sndRcvInfoSize) {
|
||||||
|
return 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
info := (*SndRcvInfo)(unsafe.Pointer(&b[0]))
|
||||||
|
n, err := c.conn.SCTPWrite(b[sndRcvInfoSize:], info)
|
||||||
|
return n + int(sndRcvInfoSize), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPSndRcvInfoWrappedConn) Read(b []byte) (int, error) {
|
||||||
|
if len(b) < int(sndRcvInfoSize) {
|
||||||
|
return 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
n, info, err := c.conn.SCTPRead(b[sndRcvInfoSize:])
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
copy(b, toBuf(info))
|
||||||
|
return n + int(sndRcvInfoSize), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPSndRcvInfoWrappedConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPSndRcvInfoWrappedConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPSndRcvInfoWrappedConn) RemoteAddr() net.Addr {
|
||||||
|
return c.conn.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPSndRcvInfoWrappedConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPSndRcvInfoWrappedConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPSndRcvInfoWrappedConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
227
vendor/github.com/ishidawataru/sctp/sctp_linux.go
generated
vendored
Normal file
227
vendor/github.com/ishidawataru/sctp/sctp_linux.go
generated
vendored
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
// +build linux,!386
|
||||||
|
|
||||||
|
package sctp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
|
||||||
|
// FIXME: syscall.SYS_SETSOCKOPT is undefined on 386
|
||||||
|
r0, r1, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT,
|
||||||
|
uintptr(fd),
|
||||||
|
SOL_SCTP,
|
||||||
|
optname,
|
||||||
|
optval,
|
||||||
|
optlen,
|
||||||
|
0)
|
||||||
|
if errno != 0 {
|
||||||
|
return r0, r1, errno
|
||||||
|
}
|
||||||
|
return r0, r1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
|
||||||
|
// FIXME: syscall.SYS_GETSOCKOPT is undefined on 386
|
||||||
|
r0, r1, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT,
|
||||||
|
uintptr(fd),
|
||||||
|
SOL_SCTP,
|
||||||
|
optname,
|
||||||
|
optval,
|
||||||
|
optlen,
|
||||||
|
0)
|
||||||
|
if errno != 0 {
|
||||||
|
return r0, r1, errno
|
||||||
|
}
|
||||||
|
return r0, r1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SCTPWrite(b []byte, info *SndRcvInfo) (int, error) {
|
||||||
|
var cbuf []byte
|
||||||
|
if info != nil {
|
||||||
|
cmsgBuf := toBuf(info)
|
||||||
|
hdr := &syscall.Cmsghdr{
|
||||||
|
Level: syscall.IPPROTO_SCTP,
|
||||||
|
Type: SCTP_CMSG_SNDRCV,
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitwidth of hdr.Len is platform-specific,
|
||||||
|
// so we use hdr.SetLen() rather than directly setting hdr.Len
|
||||||
|
hdr.SetLen(syscall.CmsgSpace(len(cmsgBuf)))
|
||||||
|
cbuf = append(toBuf(hdr), cmsgBuf...)
|
||||||
|
}
|
||||||
|
return syscall.SendmsgN(c.fd(), b, cbuf, nil, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSndRcvInfo(b []byte) (*SndRcvInfo, error) {
|
||||||
|
msgs, err := syscall.ParseSocketControlMessage(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, m := range msgs {
|
||||||
|
if m.Header.Level == syscall.IPPROTO_SCTP {
|
||||||
|
switch m.Header.Type {
|
||||||
|
case SCTP_CMSG_SNDRCV:
|
||||||
|
return (*SndRcvInfo)(unsafe.Pointer(&m.Data[0])), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SCTPRead(b []byte) (int, *SndRcvInfo, error) {
|
||||||
|
oob := make([]byte, 254)
|
||||||
|
for {
|
||||||
|
n, oobn, recvflags, _, err := syscall.Recvmsg(c.fd(), b, oob, 0)
|
||||||
|
if err != nil {
|
||||||
|
return n, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == 0 && oobn == 0 {
|
||||||
|
return 0, nil, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if recvflags&MSG_NOTIFICATION > 0 && c.notificationHandler != nil {
|
||||||
|
if err := c.notificationHandler(b[:n]); err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var info *SndRcvInfo
|
||||||
|
if oobn > 0 {
|
||||||
|
info, err = parseSndRcvInfo(oob[:oobn])
|
||||||
|
}
|
||||||
|
return n, info, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) Close() error {
|
||||||
|
if c != nil {
|
||||||
|
fd := atomic.SwapInt32(&c._fd, -1)
|
||||||
|
if fd > 0 {
|
||||||
|
info := &SndRcvInfo{
|
||||||
|
Flags: SCTP_EOF,
|
||||||
|
}
|
||||||
|
c.SCTPWrite(nil, info)
|
||||||
|
syscall.Shutdown(int(fd), syscall.SHUT_RDWR)
|
||||||
|
return syscall.Close(int(fd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return syscall.EBADF
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
|
||||||
|
af := syscall.AF_INET
|
||||||
|
switch net {
|
||||||
|
case "sctp":
|
||||||
|
hasv6 := func(addr *SCTPAddr) bool {
|
||||||
|
if addr == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, ip := range addr.IP {
|
||||||
|
if ip.To4() == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if hasv6(laddr) {
|
||||||
|
af = syscall.AF_INET6
|
||||||
|
}
|
||||||
|
case "sctp4":
|
||||||
|
case "sctp6":
|
||||||
|
af = syscall.AF_INET6
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid net: %s", net)
|
||||||
|
}
|
||||||
|
|
||||||
|
sock, err := syscall.Socket(
|
||||||
|
af,
|
||||||
|
syscall.SOCK_STREAM,
|
||||||
|
syscall.IPPROTO_SCTP,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = setNumOstreams(sock, SCTP_MAX_STREAM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if laddr != nil && len(laddr.IP) != 0 {
|
||||||
|
err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = syscall.Listen(sock, syscall.SOMAXCONN)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &SCTPListener{
|
||||||
|
fd: sock,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *SCTPListener) Accept() (net.Conn, error) {
|
||||||
|
fd, _, err := syscall.Accept4(ln.fd, 0)
|
||||||
|
return NewSCTPConn(fd, nil), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *SCTPListener) Close() error {
|
||||||
|
syscall.Shutdown(ln.fd, syscall.SHUT_RDWR)
|
||||||
|
return syscall.Close(ln.fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
|
||||||
|
af := syscall.AF_INET
|
||||||
|
switch net {
|
||||||
|
case "sctp":
|
||||||
|
hasv6 := func(addr *SCTPAddr) bool {
|
||||||
|
if addr == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, ip := range addr.IP {
|
||||||
|
if ip.To4() == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if hasv6(laddr) || hasv6(raddr) {
|
||||||
|
af = syscall.AF_INET6
|
||||||
|
}
|
||||||
|
case "sctp4":
|
||||||
|
case "sctp6":
|
||||||
|
af = syscall.AF_INET6
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid net: %s", net)
|
||||||
|
}
|
||||||
|
sock, err := syscall.Socket(
|
||||||
|
af,
|
||||||
|
syscall.SOCK_STREAM,
|
||||||
|
syscall.IPPROTO_SCTP,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = setNumOstreams(sock, SCTP_MAX_STREAM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if laddr != nil {
|
||||||
|
err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = SCTPConnect(sock, raddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewSCTPConn(sock, nil), nil
|
||||||
|
}
|
47
vendor/github.com/ishidawataru/sctp/sctp_unsupported.go
generated
vendored
Normal file
47
vendor/github.com/ishidawataru/sctp/sctp_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// +build !linux linux,386
|
||||||
|
|
||||||
|
package sctp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrUnsupported = errors.New("SCTP is unsupported on " + runtime.GOOS + "/" + runtime.GOARCH)
|
||||||
|
|
||||||
|
func setsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
|
||||||
|
return 0, 0, ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func getsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) {
|
||||||
|
return 0, 0, ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SCTPWrite(b []byte, info *SndRcvInfo) (int, error) {
|
||||||
|
return 0, ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) SCTPRead(b []byte) (int, *SndRcvInfo, error) {
|
||||||
|
return 0, nil, ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SCTPConn) Close() error {
|
||||||
|
return ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) {
|
||||||
|
return nil, ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *SCTPListener) Accept() (net.Conn, error) {
|
||||||
|
return nil, ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln *SCTPListener) Close() error {
|
||||||
|
return ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) {
|
||||||
|
return nil, ErrUnsupported
|
||||||
|
}
|
Reference in New Issue
Block a user