Compare commits

...

17 Commits

Author SHA1 Message Date
Andrew Hsu
85b4dbd3db Merge pull request #4 from thaJeztah/18.06-backport-register-oci-mediatypes
[18.06] Register OCI image media types
2018-07-05 11:52:39 -07:00
Andrew Hsu
cba80832a6 Merge pull request #3 from thaJeztah/18.06-backport-update-windows-manifest-sorting
[18.06] LCOW: Prefer Windows over Linux in a manifest list
2018-07-05 11:52:18 -07:00
Andrew Hsu
0d029b0a42 Merge pull request #2 from thaJeztah/18.06-update_go_winio
[18.06] Update Microsoft/go-winio to 0.4.8
2018-07-05 10:21:43 -07:00
Andrew Hsu
c9bfc3c842 Merge pull request #10 from thaJeztah/18.06-update_buildkit
[18.06] vendor: update buildkit to 9acf51e491
2018-07-05 08:41:47 -07:00
Tibor Vass
12eee7ce0e Merge pull request #1 from thaJeztah/18.03-update-containerd-1.1.1-rc.2
[18.06] Update containerd to v1.1.1-rc.2
2018-07-05 08:00:22 -07:00
Andrew Hsu
394fdb711c Merge pull request #7 from tiborvass/18.06-cp
[18.06] cherry-pick set for rc2
2018-07-05 02:00:07 -07:00
Tonis Tiigi
c9757e3efc vendor: update buildkit to 9acf51e491
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 6144f50e55)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-04 16:34:34 +02:00
Tonis Tiigi
3500f3f27e builder: do not send duplicate status for completed jobs
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 6f7dd9428e)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-04 16:34:28 +02:00
Tibor Vass
f030a49747 api: Change Platform field back to string (temporary workaround)
This partially reverts https://github.com/moby/moby/pull/37350

Although specs.Platform is desirable in the API, there is more work
to be done on helper functions, namely containerd's platforms.Parse
that assumes the default platform of the Go runtime.

That prevents a client to use the recommended Parse function to
retrieve a specs.Platform object.

With this change, no parsing is expected from the client.

Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit facad55744)
Signed-off-by: Tibor Vass <tibor@docker.com>
2018-07-04 00:28:41 +00:00
Andrew Hsu
d29c1fa7d1 Merge pull request #5 from thaJeztah/18.06-bump-libnetwork
[18.06] bump libnetwork to 430c00a
2018-07-03 16:15:29 -07:00
Tibor Vass
26dd527fab builder: return image ID in API when using buildkit
Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit ca8022ec63)
Signed-off-by: Tibor Vass <tibor@docker.com>
2018-07-03 22:47:03 +00:00
Chris Telfer
cc1b68d5f2 Update tests w/ new libnetwork contraints
The TestDockerNetworkIPAMMultipleNetworks test allocates several
networks simultaneously with overlapping IP addresses.  Libnetwork now
forbids this.  Adjust the test case to use distinct IP ranges for the
networks it creates.

Signed-off-by: Chris Telfer <ctelfer@docker.com>
(cherry picked from commit efb7909bef)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-03 22:25:41 +02:00
Derek McGowan
126b5bce9d Register OCI image media types
OCI types are backwards compatible with Docker manifest
types, however the media types must be registered.

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
(cherry picked from commit c4f0515837)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-03 22:25:07 +02:00
John Stephens
605cc35dc6 LCOW: Prefer Windows over Linux in a manifest list
When a manifest list contains both Linux and Windows images, always
prefer Windows when the platform OS is unspecified. Also, filter out any
Windows images with a higher build than the host, since they cannot run.

Signed-off-by: John Stephens <johnstep@docker.com>
(cherry picked from commit ddcdb7255d)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-03 22:24:27 +02:00
Sebastiaan van Stijn
d3a04c2092 Update Microsoft/go-winio to 0.4.8
Fixes named pipe support for hyper-v isolated containers

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 74095588ba)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-03 22:23:55 +02:00
Derek McGowan
bcfbeb8998 Update containerd to v1.1.1-rc.2
Signed-off-by: Derek McGowan <derek@mcgstyle.net>
(cherry picked from commit 735517928b)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-03 22:23:01 +02:00
Chris Telfer
a0c213f90c bump libnetwork to 430c00a
Bump libnetwork to 430c00a6a6b3dfdd774f21e1abd4ad6b0216c629.  This
includes the following moby-affecting changes:

 * Update vendoring for go-sockaddr (8df9f31a)
 * Fix inconsistent subnet allocation by preventing allocation of
   overlapping subnets (8579c5d2)
 * Handle IPv6 literals correctly in port bindings (474fcaf4)
 * Update vendoring for miekg/dns (8f307ac8)
 * Avoid subnet reallocation until required (9756ff7ed)
 * Bump libnetwork build to use go version 1.10.2 (603d2c1a)
 * Unwrap error type returned by PluginGetter (aacec8e1)
 * Update vendored components to match moby (d768021dd)
 * Add retry field to cluster-peers probe (dbbd06a7)
 * Fix net driver response loss on createEndpoint (1ab6e506)
   (fixes https://github.com/docker/for-linux/issues/348)

Signed-off-by: Chris Telfer <ctelfer@docker.com>
(cherry picked from commit f155f828a2)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-07-03 19:17:51 +02:00
46 changed files with 900 additions and 277 deletions

View File

@@ -73,7 +73,7 @@ func (b *Backend) Build(ctx context.Context, config backend.BuildConfig) (string
return "", err
}
if config.ProgressWriter.AuxFormatter != nil {
if err = config.ProgressWriter.AuxFormatter.Emit(types.BuildResult{ID: imageID}); err != nil {
if err = config.ProgressWriter.AuxFormatter.Emit("moby.image.id", types.BuildResult{ID: imageID}); err != nil {
return "", err
}
}

View File

@@ -14,7 +14,6 @@ import (
"strings"
"sync"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
@@ -24,8 +23,7 @@ import (
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/pkg/system"
"github.com/docker/go-units"
units "github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -72,17 +70,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
options.Target = r.FormValue("target")
options.RemoteContext = r.FormValue("remote")
if versions.GreaterThanOrEqualTo(version, "1.32") {
apiPlatform := r.FormValue("platform")
if apiPlatform != "" {
sp, err := platforms.Parse(apiPlatform)
if err != nil {
return nil, err
}
if err := system.ValidatePlatform(sp); err != nil {
return nil, err
}
options.Platform = &sp
}
options.Platform = r.FormValue("platform")
}
if r.Form.Get("shmsize") != "" {
@@ -243,6 +231,10 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
return errdefs.InvalidParameter(errors.New("squash is only supported with experimental mode"))
}
if buildOptions.Version == types.BuilderBuildKit && !br.daemon.HasExperimental() {
return errdefs.InvalidParameter(errors.New("buildkit is only supported with experimental mode"))
}
out := io.Writer(output)
if buildOptions.SuppressOutput {
out = notVerboseBuffer
@@ -255,10 +247,6 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", buildOptions.RemoteContext)
}
if buildOptions.Version == types.BuilderBuildKit && !br.daemon.HasExperimental() {
return errdefs.InvalidParameter(errors.New("buildkit is only supported with experimental mode"))
}
wantAux := versions.GreaterThanOrEqualTo(version, "1.30")
imgID, err := br.backend.Build(ctx, backend.BuildConfig{

View File

@@ -7,8 +7,7 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/go-units"
specs "github.com/opencontainers/image-spec/specs-go/v1"
units "github.com/docker/go-units"
)
// CheckpointCreateOptions holds parameters to create a checkpoint from a container
@@ -181,7 +180,7 @@ type ImageBuildOptions struct {
ExtraHosts []string // List of extra hosts
Target string
SessionID string
Platform *specs.Platform
Platform string
// Version specifies the version of the unerlying builder to use
Version BuilderVersion
// BuildID is an optional identifier that can be passed together with the

View File

@@ -644,7 +644,7 @@ func showProgress(ctx context.Context, ongoing *jobs, cs content.Store, pw progr
// featured.
type jobs struct {
name string
added map[digest.Digest]job
added map[digest.Digest]*job
mu sync.Mutex
resolved bool
}
@@ -658,7 +658,7 @@ type job struct {
func newJobs(name string) *jobs {
return &jobs{
name: name,
added: make(map[digest.Digest]job),
added: make(map[digest.Digest]*job),
}
}
@@ -669,17 +669,17 @@ func (j *jobs) add(desc ocispec.Descriptor) {
if _, ok := j.added[desc.Digest]; ok {
return
}
j.added[desc.Digest] = job{
j.added[desc.Digest] = &job{
Descriptor: desc,
started: time.Now(),
}
}
func (j *jobs) jobs() []job {
func (j *jobs) jobs() []*job {
j.mu.Lock()
defer j.mu.Unlock()
descs := make([]job, 0, len(j.added))
descs := make([]*job, 0, len(j.added))
for _, j := range j.added {
descs = append(descs, j)
}

View File

@@ -2,7 +2,6 @@ package buildkit
import (
"context"
"encoding/json"
"io"
"strings"
"sync"
@@ -14,7 +13,8 @@ import (
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/builder"
"github.com/docker/docker/daemon/images"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/pkg/system"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/control"
"github.com/moby/buildkit/identity"
@@ -209,8 +209,17 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder.
frontendAttrs["no-cache"] = ""
}
if opt.Options.Platform != nil {
frontendAttrs["platform"] = platforms.Format(*opt.Options.Platform)
if opt.Options.Platform != "" {
// same as in newBuilder in builder/dockerfile.builder.go
// TODO: remove once opt.Options.Platform is of type specs.Platform
sp, err := platforms.Parse(opt.Options.Platform)
if err != nil {
return nil, err
}
if err := system.ValidatePlatform(sp); err != nil {
return nil, err
}
frontendAttrs["platform"] = opt.Options.Platform
}
exporterAttrs := map[string]string{}
@@ -228,6 +237,8 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder.
Session: opt.Options.SessionID,
}
aux := streamformatter.AuxFormatter{opt.ProgressWriter.Output}
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error {
@@ -240,7 +251,7 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder.
return errors.Errorf("missing image id")
}
out.ImageID = id
return nil
return aux.Emit("moby.image.id", types.BuildResult{ID: id})
})
ch := make(chan *controlapi.StatusResponse)
@@ -258,25 +269,9 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder.
if err != nil {
return err
}
auxJSONBytes, err := json.Marshal(dt)
if err != nil {
if err := aux.Emit("moby.buildkit.trace", dt); err != nil {
return err
}
auxJSON := new(json.RawMessage)
*auxJSON = auxJSONBytes
msgJSON, err := json.Marshal(&jsonmessage.JSONMessage{ID: "moby.buildkit.trace", Aux: auxJSON})
if err != nil {
return err
}
msgJSON = append(msgJSON, []byte("\r\n")...)
n, err := opt.ProgressWriter.Output.Write(msgJSON)
if err != nil {
return err
}
if n != len(msgJSON) {
return io.ErrShortWrite
}
}
return nil
})

View File

@@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/container"
@@ -25,6 +26,7 @@ import (
"github.com/moby/buildkit/frontend/dockerfile/parser"
"github.com/moby/buildkit/frontend/dockerfile/shell"
"github.com/moby/buildkit/session"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sync/syncmap"
@@ -111,7 +113,11 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
PathCache: bm.pathCache,
IDMappings: bm.idMappings,
}
return newBuilder(ctx, builderOptions).build(source, dockerfile)
b, err := newBuilder(ctx, builderOptions)
if err != nil {
return nil, err
}
return b.build(source, dockerfile)
}
func (bm *BuildManager) initializeClientSession(ctx context.Context, cancel func(), options *types.ImageBuildOptions) (builder.Source, error) {
@@ -175,10 +181,11 @@ type Builder struct {
pathCache pathCache
containerManager *containerManager
imageProber ImageProber
platform *specs.Platform
}
// newBuilder creates a new Dockerfile builder from an optional dockerfile and a Options.
func newBuilder(clientCtx context.Context, options builderOptions) *Builder {
func newBuilder(clientCtx context.Context, options builderOptions) (*Builder, error) {
config := options.Options
if config == nil {
config = new(types.ImageBuildOptions)
@@ -199,7 +206,20 @@ func newBuilder(clientCtx context.Context, options builderOptions) *Builder {
containerManager: newContainerManager(options.Backend),
}
return b
// same as in Builder.Build in builder/builder-next/builder.go
// TODO: remove once config.Platform is of type specs.Platform
if config.Platform != "" {
sp, err := platforms.Parse(config.Platform)
if err != nil {
return nil, err
}
if err := system.ValidatePlatform(sp); err != nil {
return nil, err
}
b.platform = &sp
}
return b, nil
}
// Build 'LABEL' command(s) from '--label' options and add to the last stage
@@ -257,7 +277,7 @@ func emitImageID(aux *streamformatter.AuxFormatter, state *dispatchState) error
if aux == nil || state.imageID == "" {
return nil
}
return aux.Emit(types.BuildResult{ID: state.imageID})
return aux.Emit("", types.BuildResult{ID: state.imageID})
}
func processMetaArg(meta instructions.ArgCommand, shlex *shell.Lex, args *BuildArgs) error {
@@ -365,9 +385,12 @@ func BuildFromConfig(config *container.Config, changes []string, os string) (*co
return nil, errdefs.InvalidParameter(err)
}
b := newBuilder(context.Background(), builderOptions{
b, err := newBuilder(context.Background(), builderOptions{
Options: &types.ImageBuildOptions{NoCache: true},
})
if err != nil {
return nil, err
}
// ensure that the commands are valid
for _, n := range dockerfile.AST.Children {

View File

@@ -87,7 +87,7 @@ func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, i
pathCache: req.builder.pathCache,
download: download,
imageSource: imageSource,
platform: req.builder.options.Platform,
platform: req.builder.platform,
}
}

View File

@@ -146,7 +146,7 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error
imageRefOrID = stage.Image
localOnly = true
}
return d.builder.imageSources.Get(imageRefOrID, localOnly, d.builder.options.Platform)
return d.builder.imageSources.Get(imageRefOrID, localOnly, d.builder.platform)
}
// FROM [--platform=platform] imagename[:tag | @digest] [AS build-stage-name]
@@ -238,7 +238,7 @@ func (d *dispatchRequest) getImageOrStage(name string, platform *specs.Platform)
}
if platform == nil {
platform = d.builder.options.Platform
platform = d.builder.platform
}
// Windows cannot support a container with no base image unless it is LCOW.

View File

@@ -6,7 +6,6 @@ import (
"runtime"
"testing"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/container"
@@ -23,8 +22,7 @@ import (
func newBuilderWithMockBackend() *Builder {
mockBackend := &MockBackend{}
defaultPlatform := platforms.DefaultSpec()
opts := &types.ImageBuildOptions{Platform: &defaultPlatform}
opts := &types.ImageBuildOptions{}
ctx := context.Background()
b := &Builder{
options: opts,
@@ -116,7 +114,7 @@ func TestFromScratch(t *testing.T) {
err := initializeStage(sb, cmd)
if runtime.GOOS == "windows" && !system.LCOWSupported() {
assert.Check(t, is.Error(err, "Windows does not support FROM scratch"))
assert.Check(t, is.Error(err, "Linux containers are not supported on this system"))
return
}

View File

@@ -169,7 +169,7 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
return err
}
imageMount, err := b.imageSources.Get(state.imageID, true, req.builder.options.Platform)
imageMount, err := b.imageSources.Get(state.imageID, true, req.builder.platform)
if err != nil {
return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
}
@@ -416,7 +416,9 @@ func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *contai
func (b *Builder) create(runConfig *container.Config) (string, error) {
logrus.Debugf("[BUILDER] Command to be executed: %v", runConfig.Cmd)
hostConfig := hostConfigFromOptions(b.options)
isWCOW := runtime.GOOS == "windows" && b.platform != nil && b.platform.OS == "windows"
hostConfig := hostConfigFromOptions(b.options, isWCOW)
container, err := b.containerManager.Create(runConfig, hostConfig)
if err != nil {
return "", err
@@ -429,7 +431,7 @@ func (b *Builder) create(runConfig *container.Config) (string, error) {
return container.ID, nil
}
func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConfig {
func hostConfigFromOptions(options *types.ImageBuildOptions, isWCOW bool) *container.HostConfig {
resources := container.Resources{
CgroupParent: options.CgroupParent,
CPUShares: options.CPUShares,
@@ -457,7 +459,7 @@ func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConf
// is too small for builder scenarios where many users are
// using RUN statements to install large amounts of data.
// Use 127GB as that's the default size of a VHD in Hyper-V.
if runtime.GOOS == "windows" && options.Platform != nil && options.Platform.OS == "windows" {
if isWCOW {
hc.StorageOpt = make(map[string]string)
hc.StorageOpt["size"] = "127GB"
}

View File

@@ -8,8 +8,8 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
)
@@ -30,12 +30,6 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio
}
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
if options.Platform != nil {
if err := cli.NewVersionError("1.32", "platform"); err != nil {
return types.ImageBuildResponse{}, err
}
query.Set("platform", platforms.Format(*options.Platform))
}
headers.Set("Content-Type", "application/x-tar")
serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers)
@@ -130,8 +124,11 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur
if options.SessionID != "" {
query.Set("session", options.SessionID)
}
if options.Platform != nil {
query.Set("platform", platforms.Format(*options.Platform))
if options.Platform != "" {
if err := cli.NewVersionError("1.32", "platform"); err != nil {
return query, err
}
query.Set("platform", strings.ToLower(options.Platform))
}
if options.BuildID != "" {
query.Set("buildid", options.BuildID)

45
distribution/oci.go Normal file
View File

@@ -0,0 +1,45 @@
package distribution
import (
"fmt"
"github.com/docker/distribution"
"github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema2"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
func init() {
// TODO: Remove this registration if distribution is included with OCI support
ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
m := new(schema2.DeserializedManifest)
err := m.UnmarshalJSON(b)
if err != nil {
return nil, distribution.Descriptor{}, err
}
dgst := digest.FromBytes(b)
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: ocispec.MediaTypeImageManifest}, err
}
err := distribution.RegisterManifestSchema(ocispec.MediaTypeImageManifest, ocischemaFunc)
if err != nil {
panic(fmt.Sprintf("Unable to register manifest: %s", err))
}
manifestListFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
m := new(manifestlist.DeserializedManifestList)
err := m.UnmarshalJSON(b)
if err != nil {
return nil, distribution.Descriptor{}, err
}
dgst := digest.FromBytes(b)
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: ocispec.MediaTypeImageIndex}, err
}
err = distribution.RegisterManifestSchema(ocispec.MediaTypeImageIndex, manifestListFunc)
if err != nil {
panic(fmt.Sprintf("Unable to register manifest: %s", err))
}
}

View File

@@ -74,11 +74,14 @@ func filterManifests(manifests []manifestlist.ManifestDescriptor, p specs.Platfo
if (manifestDescriptor.Platform.Architecture == runtime.GOARCH) &&
((p.OS != "" && manifestDescriptor.Platform.OS == p.OS) || // Explicit user request for an OS we know we support
(p.OS == "" && system.IsOSSupported(manifestDescriptor.Platform.OS))) { // No user requested OS, but one we can support
matches = append(matches, manifestDescriptor)
logrus.Debugf("found match %s/%s %s with media type %s, digest %s", manifestDescriptor.Platform.OS, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
if strings.EqualFold("windows", manifestDescriptor.Platform.OS) {
if err := checkImageCompatibility("windows", manifestDescriptor.Platform.OSVersion); err != nil {
continue
}
foundWindowsMatch = true
}
matches = append(matches, manifestDescriptor)
logrus.Debugf("found match %s/%s %s with media type %s, digest %s", manifestDescriptor.Platform.OS, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
} else {
logrus.Debugf("ignoring %s/%s %s with media type %s, digest %s", manifestDescriptor.Platform.OS, manifestDescriptor.Platform.Architecture, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
}
@@ -103,7 +106,8 @@ func (mbv manifestsByVersion) Less(i, j int) bool {
// TODO: Split version by parts and compare
// TODO: Prefer versions which have a greater version number
// Move compatible versions to the top, with no other ordering changes
return versionMatch(mbv.list[i].Platform.OSVersion, mbv.version) && !versionMatch(mbv.list[j].Platform.OSVersion, mbv.version)
return (strings.EqualFold("windows", mbv.list[i].Platform.OS) && !strings.EqualFold("windows", mbv.list[j].Platform.OS)) ||
(versionMatch(mbv.list[i].Platform.OSVersion, mbv.version) && !versionMatch(mbv.list[j].Platform.OSVersion, mbv.version))
}
func (mbv manifestsByVersion) Len() int {

View File

@@ -17,11 +17,13 @@ import (
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/registry"
"github.com/docker/go-connections/sockets"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// ImageTypes represents the schema2 config types for images
var ImageTypes = []string{
schema2.MediaTypeImageConfig,
ocispec.MediaTypeImageConfig,
// Handle unexpected values from https://github.com/docker/distribution/issues/1621
// (see also https://github.com/docker/docker/issues/22378,
// https://github.com/docker/docker/issues/30083)

View File

@@ -4,7 +4,7 @@
# containerd is also pinned in vendor.conf. When updating the binary
# version you may also need to update the vendor version to pick up bug
# fixes or new APIs.
CONTAINERD_COMMIT=cbef57047e900aeb2bafe7a634919bec13f4a2a5 # v1.1.1-rc.1
CONTAINERD_COMMIT=e5fb877b9f6c14b15f48643735a8c9764a7319d3 # v1.1.1-rc.2
install_containerd() {
echo "Install containerd version $CONTAINERD_COMMIT"

View File

@@ -3,7 +3,7 @@
# LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When
# updating the binary version, consider updating github.com/docker/libnetwork
# in vendor.conf accordingly
LIBNETWORK_COMMIT=19279f0492417475b6bfbd0aa529f73e8f178fb5
LIBNETWORK_COMMIT=430c00a6a6b3dfdd774f21e1abd4ad6b0216c629
install_proxy() {
case "$1" in

View File

@@ -610,17 +610,17 @@ func (s *DockerNetworkSuite) TestDockerNetworkIPAMMultipleNetworks(c *check.C) {
// test network with multiple subnets
// bridge network doesn't support multiple subnets. hence, use a dummy driver that supports
dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, "--subnet=192.168.0.0/16", "--subnet=192.170.0.0/16", "test6")
dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, "--subnet=192.170.0.0/16", "--subnet=192.171.0.0/16", "test6")
assertNwIsAvailable(c, "test6")
// test network with multiple subnets with valid ipam combinations
// also check same subnet across networks when the driver supports it.
dockerCmd(c, "network", "create", "-d", dummyNetworkDriver,
"--subnet=192.168.0.0/16", "--subnet=192.170.0.0/16",
"--gateway=192.168.0.100", "--gateway=192.170.0.100",
"--ip-range=192.168.1.0/24",
"--aux-address", "a=192.168.1.5", "--aux-address", "b=192.168.1.6",
"--aux-address", "c=192.170.1.5", "--aux-address", "d=192.170.1.6",
"--subnet=192.172.0.0/16", "--subnet=192.173.0.0/16",
"--gateway=192.172.0.100", "--gateway=192.173.0.100",
"--ip-range=192.172.1.0/24",
"--aux-address", "a=192.172.1.5", "--aux-address", "b=192.172.1.6",
"--aux-address", "c=192.173.1.5", "--aux-address", "d=192.173.1.6",
"test7")
assertNwIsAvailable(c, "test7")

View File

@@ -139,14 +139,14 @@ type AuxFormatter struct {
}
// Emit emits the given interface as an aux progress message
func (sf *AuxFormatter) Emit(aux interface{}) error {
func (sf *AuxFormatter) Emit(id string, aux interface{}) error {
auxJSONBytes, err := json.Marshal(aux)
if err != nil {
return err
}
auxJSON := new(json.RawMessage)
*auxJSON = auxJSONBytes
msgJSON, err := json.Marshal(&jsonmessage.JSONMessage{Aux: auxJSON})
msgJSON, err := json.Marshal(&jsonmessage.JSONMessage{ID: id, Aux: auxJSON})
if err != nil {
return err
}

View File

@@ -106,7 +106,7 @@ func TestAuxFormatterEmit(t *testing.T) {
sampleAux := &struct {
Data string
}{"Additional data"}
err := aux.Emit(sampleAux)
err := aux.Emit("", sampleAux)
assert.NilError(t, err)
assert.Check(t, is.Equal(`{"aux":{"Data":"Additional data"}}`+streamNewline, b.String()))
}

View File

@@ -1,7 +1,7 @@
# the following lines are in sorted order, FYI
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
github.com/Microsoft/hcsshim v0.6.11
github.com/Microsoft/go-winio v0.4.7
github.com/Microsoft/go-winio v0.4.8
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git
github.com/golang/gddo 9b12a26f3fbd7397dee4e20939ddca719d840d2a
@@ -26,7 +26,7 @@ github.com/imdario/mergo v0.3.5
golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5
# buildkit
github.com/moby/buildkit cce2080ddbe4698912f2290892b247c83627efa8
github.com/moby/buildkit 9acf51e49185b348608e0096b2903dd72907adcb
github.com/tonistiigi/fsutil 8abad97ee3969cdf5e9c367f46adba2c212b3ddb
github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
@@ -37,14 +37,14 @@ github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b
#get libnetwork packages
# When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly
github.com/docker/libnetwork 19279f0492417475b6bfbd0aa529f73e8f178fb5
github.com/docker/libnetwork 430c00a6a6b3dfdd774f21e1abd4ad6b0216c629
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
github.com/hashicorp/memberlist 3d8438da9589e7b608a83ffac1ef8211486bcb7c
github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
github.com/hashicorp/go-sockaddr 6d291a969b86c4b633730bfc6b8b9d64c3aafed9
github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef

View File

@@ -121,6 +121,11 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
// zero-byte message, ensure that all future Read() calls
// also return EOF.
f.readEOF = true
} else if err == syscall.ERROR_MORE_DATA {
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
// and the message still has more bytes. Treat this as a success, since
// this package presents all named pipes as byte streams.
err = nil
}
return n, err
}
@@ -175,16 +180,6 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
return nil, err
}
var state uint32
err = getNamedPipeHandleState(h, &state, nil, nil, nil, nil, 0)
if err != nil {
return nil, err
}
if state&cPIPE_READMODE_MESSAGE != 0 {
return nil, &os.PathError{Op: "open", Path: path, Err: errors.New("message readmode pipes not supported")}
}
f, err := makeWin32File(h)
if err != nil {
syscall.Close(h)

View File

@@ -69,6 +69,7 @@ import (
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/osl"
"github.com/docker/libnetwork/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -1252,7 +1253,7 @@ func (c *controller) loadDriver(networkType string) error {
}
if err != nil {
if err == plugins.ErrNotFound {
if errors.Cause(err) == plugins.ErrNotFound {
return types.NotFoundErrorf(err.Error())
}
return err

View File

@@ -29,7 +29,10 @@ const (
// Allocator provides per address space ipv4/ipv6 book keeping
type Allocator struct {
// Predefined pools for default address spaces
predefined map[string][]*net.IPNet
// Separate from the addrSpace because they should not be serialized
predefined map[string][]*net.IPNet
predefinedStartIndices map[string]int
// The (potentially serialized) address spaces
addrSpaces map[string]*addrSpace
// stores []datastore.Datastore
// Allocated addresses in each address space's subnet
@@ -47,6 +50,9 @@ func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) {
globalAddressSpace: ipamutils.PredefinedGranularNetworks,
}
// Initialize asIndices map
a.predefinedStartIndices = make(map[string]int)
// Initialize bitseq map
a.addresses = make(map[SubnetKey]*bitseq.Handle)
@@ -374,11 +380,24 @@ func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle,
func (a *Allocator) getPredefineds(as string) []*net.IPNet {
a.Lock()
defer a.Unlock()
l := make([]*net.IPNet, 0, len(a.predefined[as]))
for _, pool := range a.predefined[as] {
l = append(l, pool)
p := a.predefined[as]
i := a.predefinedStartIndices[as]
// defensive in case the list changed since last update
if i >= len(p) {
i = 0
}
return l
return append(p[i:], p[:i]...)
}
func (a *Allocator) updateStartIndex(as string, amt int) {
a.Lock()
i := a.predefinedStartIndices[as] + amt
if i < 0 || i >= len(a.predefined[as]) {
i = 0
}
a.predefinedStartIndices[as] = i
a.Unlock()
}
func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) {
@@ -397,21 +416,26 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
return nil, err
}
for _, nw := range a.getPredefineds(as) {
predefined := a.getPredefineds(as)
aSpace.Lock()
for i, nw := range predefined {
if v != getAddressVersion(nw.IP) {
continue
}
aSpace.Lock()
// Checks whether pool has already been allocated
if _, ok := aSpace.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]; ok {
aSpace.Unlock()
continue
}
// Shouldn't be necessary, but check prevents IP collisions should
// predefined pools overlap for any reason.
if !aSpace.contains(as, nw) {
aSpace.Unlock()
a.updateStartIndex(as, i+1)
return nw, nil
}
aSpace.Unlock()
}
aSpace.Unlock()
return nil, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)
}

View File

@@ -262,12 +262,13 @@ func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *Addr
defer aSpace.Unlock()
// Check if already allocated
if p, ok := aSpace.subnets[k]; ok {
if _, ok := aSpace.subnets[k]; ok {
if pdf {
return nil, types.InternalMaskableErrorf("predefined pool %s is already reserved", nw)
}
aSpace.incRefCount(p, 1)
return func() error { return nil }, nil
// This means the same pool is already allocated. updatePoolDBOnAdd is called when there
// is request for a pool/subpool. It should ensure there is no overlap with existing pools
return nil, ipamapi.ErrPoolOverlap
}
// If master pool, check for overlap

View File

@@ -1156,18 +1156,6 @@ func (n *network) createEndpoint(name string, options ...EndpointOption) (Endpoi
ep.releaseAddress()
}
}()
// Moving updateToSTore before calling addEndpoint so that we shall clean up VETH interfaces in case
// DockerD get killed between addEndpoint and updateSTore call
if err = n.getController().updateToStore(ep); err != nil {
return nil, err
}
defer func() {
if err != nil {
if e := n.getController().deleteFromStore(ep); e != nil {
logrus.Warnf("error rolling back endpoint %s from store: %v", name, e)
}
}
}()
if err = n.addEndpoint(ep); err != nil {
return nil, err
@@ -1180,6 +1168,19 @@ func (n *network) createEndpoint(name string, options ...EndpointOption) (Endpoi
}
}()
// We should perform updateToStore call right after addEndpoint
// in order to have iface properly configured
if err = n.getController().updateToStore(ep); err != nil {
return nil, err
}
defer func() {
if err != nil {
if e := n.getController().deleteFromStore(ep); e != nil {
logrus.Warnf("error rolling back endpoint %s from store: %v", name, e)
}
}
}()
if err = ep.assignAddress(ipam, false, n.enableIPv6 && n.postIPv6); err != nil {
return nil, err
}

View File

@@ -41,7 +41,7 @@ func (nDB *NetworkDB) handleNodeEvent(nEvent *NodeEvent) bool {
// If the node is not known from memberlist we cannot process save any state of it else if it actually
// dies we won't receive any notification and we will remain stuck with it
if _, ok := nDB.nodes[nEvent.NodeName]; !ok {
logrus.Error("node: %s is unknown to memberlist", nEvent.NodeName)
logrus.Errorf("node: %s is unknown to memberlist", nEvent.NodeName)
return false
}

View File

@@ -145,7 +145,12 @@ func (p *PortBinding) String() string {
return ret
}
// FromString reads the PortBinding structure from string
// 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 {
@@ -167,21 +172,19 @@ func (p *PortBinding) FromString(s string) error {
}
func parseIPPort(s string) (net.IP, uint16, error) {
pp := strings.Split(s, ":")
if len(pp) != 2 {
return nil, 0, BadRequestErrorf("invalid format: %s", s)
}
var ip net.IP
if pp[0] != "" {
if ip = net.ParseIP(pp[0]); ip == nil {
return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0])
}
}
port, err := strconv.ParseUint(pp[1], 10, 16)
hoststr, portstr, err := net.SplitHostPort(s)
if err != nil {
return nil, 0, BadRequestErrorf("invalid port: %s", pp[1])
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

View File

@@ -1,27 +1,28 @@
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/Microsoft/go-winio v0.4.5
github.com/Microsoft/hcsshim v0.6.5
github.com/Microsoft/go-winio v0.4.7
github.com/Microsoft/hcsshim v0.6.11
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
github.com/codegangsta/cli a65b733b303f0055f8d324d805f393cd3e7a7904
github.com/containerd/console 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e
github.com/containerd/continuity 22694c680ee48fb8f50015b44618517e2bde77e8
github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925
github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
github.com/coreos/etcd v3.2.1
github.com/coreos/go-semver v0.2.0
github.com/coreos/go-systemd v4
github.com/coreos/go-systemd v17
github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8
github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
github.com/docker/docker a3efe9722f34af5cf4443fe3a5c4e4e3e0457b54
github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
github.com/docker/docker 162ba6016def672690ee4a1f3978368853a1e149
github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
github.com/godbus/dbus v4.0.0
github.com/gogo/protobuf v0.4
github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
github.com/gogo/protobuf v1.0.0
github.com/golang/protobuf v1.1.0
github.com/gorilla/context v1.1
github.com/gorilla/mux v1.1
github.com/hashicorp/consul v0.5.2
@@ -29,27 +30,37 @@ github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
github.com/hashicorp/memberlist 3d8438da9589e7b608a83ffac1ef8211486bcb7c
github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
github.com/hashicorp/go-sockaddr 6d291a969b86c4b633730bfc6b8b9d64c3aafed9
github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
github.com/mattn/go-shellwords v1.0.3
github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7
github.com/miekg/dns v1.0.7
github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13
github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d
github.com/opencontainers/runtime-spec v1.0.0
github.com/opencontainers/selinux v1.0.0-rc1
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340
github.com/opencontainers/runtime-spec v1.0.1
github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd
github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
github.com/sirupsen/logrus v1.0.3
github.com/stretchr/testify dab07ac62d4905d3e48d17dc549c684ac3b7c15a
github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
github.com/syndtr/gocapability 33e07d32887e1e06b7c025f27ce52f62c7990bc0
github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e
github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
golang.org/x/crypto 1a580b3eff7814fc9b40602fd35256c63b50f491
golang.org/x/net 0ed95abb35c445290478a5348a7b38bb154135fd
golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd
golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb
github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
github.com/cyphar/filepath-securejoin v0.2.1
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
github.com/hashicorp/go-immutable-radix 7f3cd4390caab3250a57f30efdb2a65dd7649ecf
github.com/hashicorp/golang-lru 0fb14efe8c47ae851c0034ed7a448854d3d34cf3
github.com/hashicorp/go-cleanhttp d5fe4b57a186c716b0e00b8c301cbd9b4182694d
github.com/hashicorp/go-rootcerts 6bb64b370b90e7ef1fa532be9e591a81c3493e00
github.com/mitchellh/go-homedir 3864e76763d94a6df2f9960b16a20a33da9f9a66

View File

@@ -1,5 +1,7 @@
package sockaddr
import "strings"
// ifAddrAttrMap is a map of the IfAddr type-specific attributes.
var ifAddrAttrMap map[AttrName]func(IfAddr) string
var ifAddrAttrs []AttrName
@@ -30,6 +32,53 @@ func GetPrivateIP() (string, error) {
return ip.NetIP().String(), nil
}
// GetPrivateIPs returns a string with all IP addresses that are part of RFC
// 6890 (regardless of whether or not there is a default route, unlike
// GetPublicIP). If the system can't find any RFC 6890 IP addresses, an empty
// string will be returned instead. This function is the `eval` equivalent of:
//
// ```
// $ sockaddr eval -r '{{GetAllInterfaces | include "RFC" "6890" | join "address" " "}}'
/// ```
func GetPrivateIPs() (string, error) {
ifAddrs, err := GetAllInterfaces()
if err != nil {
return "", err
} else if len(ifAddrs) < 1 {
return "", nil
}
ifAddrs, _ = FilterIfByType(ifAddrs, TypeIP)
if len(ifAddrs) == 0 {
return "", nil
}
OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(ifAddrs)
ifAddrs, _, err = IfByRFC("6890", ifAddrs)
if err != nil {
return "", err
} else if len(ifAddrs) == 0 {
return "", nil
}
_, ifAddrs, err = IfByRFC(ForwardingBlacklistRFC, ifAddrs)
if err != nil {
return "", err
} else if len(ifAddrs) == 0 {
return "", nil
}
ips := make([]string, 0, len(ifAddrs))
for _, ifAddr := range ifAddrs {
ip := *ToIPAddr(ifAddr.SockAddr)
s := ip.NetIP().String()
ips = append(ips, s)
}
return strings.Join(ips, " "), nil
}
// GetPublicIP returns a string with a single IP address that is NOT part of RFC
// 6890 and has a default route. If the system can't determine its IP address
// or find a non RFC 6890 IP address, an empty string will be returned instead.
@@ -51,6 +100,47 @@ func GetPublicIP() (string, error) {
return ip.NetIP().String(), nil
}
// GetPublicIPs returns a string with all IP addresses that are NOT part of RFC
// 6890 (regardless of whether or not there is a default route, unlike
// GetPublicIP). If the system can't find any non RFC 6890 IP addresses, an
// empty string will be returned instead. This function is the `eval`
// equivalent of:
//
// ```
// $ sockaddr eval -r '{{GetAllInterfaces | exclude "RFC" "6890" | join "address" " "}}'
/// ```
func GetPublicIPs() (string, error) {
ifAddrs, err := GetAllInterfaces()
if err != nil {
return "", err
} else if len(ifAddrs) < 1 {
return "", nil
}
ifAddrs, _ = FilterIfByType(ifAddrs, TypeIP)
if len(ifAddrs) == 0 {
return "", nil
}
OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(ifAddrs)
_, ifAddrs, err = IfByRFC("6890", ifAddrs)
if err != nil {
return "", err
} else if len(ifAddrs) == 0 {
return "", nil
}
ips := make([]string, 0, len(ifAddrs))
for _, ifAddr := range ifAddrs {
ip := *ToIPAddr(ifAddr.SockAddr)
s := ip.NetIP().String()
ips = append(ips, s)
}
return strings.Join(ips, " "), nil
}
// GetInterfaceIP returns a string with a single IP address sorted by the size
// of the network (i.e. IP addresses with a smaller netmask, larger network
// size, are sorted first). This function is the `eval` equivalent of:
@@ -91,6 +181,44 @@ func GetInterfaceIP(namedIfRE string) (string, error) {
return IPAddrAttr(*ip, "address"), nil
}
// GetInterfaceIPs returns a string with all IPs, sorted by the size of the
// network (i.e. IP addresses with a smaller netmask, larger network size, are
// sorted first), on a named interface. This function is the `eval` equivalent
// of:
//
// ```
// $ sockaddr eval -r '{{GetAllInterfaces | include "name" <<ARG>> | sort "type,size" | join "address" " "}}'
/// ```
func GetInterfaceIPs(namedIfRE string) (string, error) {
ifAddrs, err := GetAllInterfaces()
if err != nil {
return "", err
}
ifAddrs, _, err = IfByName(namedIfRE, ifAddrs)
if err != nil {
return "", err
}
ifAddrs, err = SortIfBy("+type,+size", ifAddrs)
if err != nil {
return "", err
}
if len(ifAddrs) == 0 {
return "", err
}
ips := make([]string, 0, len(ifAddrs))
for _, ifAddr := range ifAddrs {
ip := *ToIPAddr(ifAddr.SockAddr)
s := ip.NetIP().String()
ips = append(ips, s)
}
return strings.Join(ips, " "), nil
}
// IfAddrAttrs returns a list of attributes supported by the IfAddr type
func IfAddrAttrs() []AttrName {
return ifAddrAttrs

View File

@@ -1,8 +1,10 @@
package sockaddr
import (
"encoding/binary"
"errors"
"fmt"
"math/big"
"net"
"regexp"
"sort"
@@ -10,6 +12,14 @@ import (
"strings"
)
var (
// Centralize all regexps and regexp.Copy() where necessary.
signRE *regexp.Regexp = regexp.MustCompile(`^[\s]*[+-]`)
whitespaceRE *regexp.Regexp = regexp.MustCompile(`[\s]+`)
ifNameRE *regexp.Regexp = regexp.MustCompile(`^(?:Ethernet|Wireless LAN) adapter ([^:]+):`)
ipAddrRE *regexp.Regexp = regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
)
// IfAddrs is a slice of IfAddr
type IfAddrs []IfAddr
@@ -91,6 +101,40 @@ func AscIfAddress(p1Ptr, p2Ptr *IfAddr) int {
return AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
}
// AscIfDefault is a sorting function to sort IfAddrs by whether or not they
// have a default route or not. Non-equal types are deferred in the sort.
//
// FIXME: This is a particularly expensive sorting operation because of the
// non-memoized calls to NewRouteInfo(). In an ideal world the routeInfo data
// once at the start of the sort and pass it along as a context or by wrapping
// the IfAddr type with this information (this would also solve the inability to
// return errors and the possibility of failing silently). Fortunately,
// N*log(N) where N = 3 is only ~6.2 invocations. Not ideal, but not worth
// optimizing today. The common case is this gets called once or twice.
// Patches welcome.
func AscIfDefault(p1Ptr, p2Ptr *IfAddr) int {
ri, err := NewRouteInfo()
if err != nil {
return sortDeferDecision
}
defaultIfName, err := ri.GetDefaultInterfaceName()
if err != nil {
return sortDeferDecision
}
switch {
case p1Ptr.Interface.Name == defaultIfName && p2Ptr.Interface.Name == defaultIfName:
return sortDeferDecision
case p1Ptr.Interface.Name == defaultIfName:
return sortReceiverBeforeArg
case p2Ptr.Interface.Name == defaultIfName:
return sortArgBeforeReceiver
default:
return sortDeferDecision
}
}
// AscIfName is a sorting function to sort IfAddrs by their interface names.
func AscIfName(p1Ptr, p2Ptr *IfAddr) int {
return strings.Compare(p1Ptr.Name, p2Ptr.Name)
@@ -127,6 +171,11 @@ func DescIfAddress(p1Ptr, p2Ptr *IfAddr) int {
return -1 * AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
}
// DescIfDefault is identical to AscIfDefault but reverse ordered.
func DescIfDefault(p1Ptr, p2Ptr *IfAddr) int {
return -1 * AscIfDefault(p1Ptr, p2Ptr)
}
// DescIfName is identical to AscIfName but reverse ordered.
func DescIfName(p1Ptr, p2Ptr *IfAddr) int {
return -1 * strings.Compare(p1Ptr.Name, p2Ptr.Name)
@@ -169,7 +218,15 @@ func FilterIfByType(ifAddrs IfAddrs, type_ SockAddrType) (matchedIfs, excludedIf
// IfAttr forwards the selector to IfAttr.Attr() for resolution. If there is
// more than one IfAddr, only the first IfAddr is used.
func IfAttr(selectorName string, ifAddrs IfAddrs) (string, error) {
func IfAttr(selectorName string, ifAddr IfAddr) (string, error) {
attrName := AttrName(strings.ToLower(selectorName))
attrVal, err := ifAddr.Attr(attrName)
return attrVal, err
}
// IfAttrs forwards the selector to IfAttrs.Attr() for resolution. If there is
// more than one IfAddr, only the first IfAddr is used.
func IfAttrs(selectorName string, ifAddrs IfAddrs) (string, error) {
if len(ifAddrs) == 0 {
return "", nil
}
@@ -243,10 +300,10 @@ func GetDefaultInterfaces() (IfAddrs, error) {
// the `eval` equivalent of:
//
// ```
// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | include "RFC" "6890" }}'
// $ sockaddr eval -r '{{GetAllInterfaces | include "type" "ip" | include "flags" "forwardable" | include "flags" "up" | sort "default,type,size" | include "RFC" "6890" }}'
/// ```
func GetPrivateInterfaces() (IfAddrs, error) {
privateIfs, err := GetDefaultInterfaces()
privateIfs, err := GetAllInterfaces()
if err != nil {
return IfAddrs{}, err
}
@@ -259,15 +316,21 @@ func GetPrivateInterfaces() (IfAddrs, error) {
return IfAddrs{}, nil
}
privateIfs, _, err = IfByFlag("forwardable|up", privateIfs)
privateIfs, _, err = IfByFlag("forwardable", privateIfs)
if err != nil {
return IfAddrs{}, err
}
privateIfs, _, err = IfByFlag("up", privateIfs)
if err != nil {
return IfAddrs{}, err
}
if len(privateIfs) == 0 {
return IfAddrs{}, nil
}
OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(privateIfs)
OrderedIfAddrBy(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(privateIfs)
privateIfs, _, err = IfByRFC("6890", privateIfs)
if err != nil {
@@ -285,10 +348,10 @@ func GetPrivateInterfaces() (IfAddrs, error) {
// function is the `eval` equivalent of:
//
// ```
// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | exclude "RFC" "6890" }}'
// $ sockaddr eval -r '{{GetAllInterfaces | include "type" "ip" | include "flags" "forwardable" | include "flags" "up" | sort "default,type,size" | exclude "RFC" "6890" }}'
/// ```
func GetPublicInterfaces() (IfAddrs, error) {
publicIfs, err := GetDefaultInterfaces()
publicIfs, err := GetAllInterfaces()
if err != nil {
return IfAddrs{}, err
}
@@ -301,15 +364,21 @@ func GetPublicInterfaces() (IfAddrs, error) {
return IfAddrs{}, nil
}
publicIfs, _, err = IfByFlag("forwardable|up", publicIfs)
publicIfs, _, err = IfByFlag("forwardable", publicIfs)
if err != nil {
return IfAddrs{}, err
}
publicIfs, _, err = IfByFlag("up", publicIfs)
if err != nil {
return IfAddrs{}, err
}
if len(publicIfs) == 0 {
return IfAddrs{}, nil
}
OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(publicIfs)
OrderedIfAddrBy(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(publicIfs)
_, publicIfs, err = IfByRFC("6890", publicIfs)
if err != nil {
@@ -652,6 +721,245 @@ func IfByNetwork(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, IfAddrs,
return includedIfs, excludedIfs, nil
}
// IfAddrMath will return a new IfAddr struct with a mutated value.
func IfAddrMath(operation, value string, inputIfAddr IfAddr) (IfAddr, error) {
// Regexp used to enforce the sign being a required part of the grammar for
// some values.
signRe := signRE.Copy()
switch strings.ToLower(operation) {
case "address":
// "address" operates on the IP address and is allowed to overflow or
// underflow networks, however it will wrap along the underlying address's
// underlying type.
if !signRe.MatchString(value) {
return IfAddr{}, fmt.Errorf("sign (+/-) is required for operation %q", operation)
}
switch sockType := inputIfAddr.SockAddr.Type(); sockType {
case TypeIPv4:
// 33 == Accept any uint32 value
// TODO(seanc@): Add the ability to parse hex
i, err := strconv.ParseInt(value, 10, 33)
if err != nil {
return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
}
ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr)
ipv4Uint32 := uint32(ipv4.Address)
ipv4Uint32 += uint32(i)
return IfAddr{
SockAddr: IPv4Addr{
Address: IPv4Address(ipv4Uint32),
Mask: ipv4.Mask,
},
Interface: inputIfAddr.Interface,
}, nil
case TypeIPv6:
// 64 == Accept any int32 value
// TODO(seanc@): Add the ability to parse hex. Also parse a bignum int.
i, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
}
ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr)
ipv6BigIntA := new(big.Int)
ipv6BigIntA.Set(ipv6.Address)
ipv6BigIntB := big.NewInt(i)
ipv6Addr := ipv6BigIntA.Add(ipv6BigIntA, ipv6BigIntB)
ipv6Addr.And(ipv6Addr, ipv6HostMask)
return IfAddr{
SockAddr: IPv6Addr{
Address: IPv6Address(ipv6Addr),
Mask: ipv6.Mask,
},
Interface: inputIfAddr.Interface,
}, nil
default:
return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType)
}
case "network":
// "network" operates on the network address. Positive values start at the
// network address and negative values wrap at the network address, which
// means a "-1" value on a network will be the broadcast address after
// wrapping is applied.
if !signRe.MatchString(value) {
return IfAddr{}, fmt.Errorf("sign (+/-) is required for operation %q", operation)
}
switch sockType := inputIfAddr.SockAddr.Type(); sockType {
case TypeIPv4:
// 33 == Accept any uint32 value
// TODO(seanc@): Add the ability to parse hex
i, err := strconv.ParseInt(value, 10, 33)
if err != nil {
return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
}
ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr)
ipv4Uint32 := uint32(ipv4.NetworkAddress())
// Wrap along network mask boundaries. EZ-mode wrapping made possible by
// use of int64 vs a uint.
var wrappedMask int64
if i >= 0 {
wrappedMask = i
} else {
wrappedMask = 1 + i + int64(^uint32(ipv4.Mask))
}
ipv4Uint32 = ipv4Uint32 + (uint32(wrappedMask) &^ uint32(ipv4.Mask))
return IfAddr{
SockAddr: IPv4Addr{
Address: IPv4Address(ipv4Uint32),
Mask: ipv4.Mask,
},
Interface: inputIfAddr.Interface,
}, nil
case TypeIPv6:
// 64 == Accept any int32 value
// TODO(seanc@): Add the ability to parse hex. Also parse a bignum int.
i, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
}
ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr)
ipv6BigInt := new(big.Int)
ipv6BigInt.Set(ipv6.NetworkAddress())
mask := new(big.Int)
mask.Set(ipv6.Mask)
if i > 0 {
wrappedMask := new(big.Int)
wrappedMask.SetInt64(i)
wrappedMask.AndNot(wrappedMask, mask)
ipv6BigInt.Add(ipv6BigInt, wrappedMask)
} else {
// Mask off any bits that exceed the network size. Subtract the
// wrappedMask from the last usable - 1
wrappedMask := new(big.Int)
wrappedMask.SetInt64(-1 * i)
wrappedMask.Sub(wrappedMask, big.NewInt(1))
wrappedMask.AndNot(wrappedMask, mask)
lastUsable := new(big.Int)
lastUsable.Set(ipv6.LastUsable().(IPv6Addr).Address)
ipv6BigInt = lastUsable.Sub(lastUsable, wrappedMask)
}
return IfAddr{
SockAddr: IPv6Addr{
Address: IPv6Address(ipv6BigInt),
Mask: ipv6.Mask,
},
Interface: inputIfAddr.Interface,
}, nil
default:
return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType)
}
case "mask":
// "mask" operates on the IP address and returns the IP address on
// which the given integer mask has been applied. If the applied mask
// corresponds to a larger network than the mask of the IP address,
// the latter will be replaced by the former.
switch sockType := inputIfAddr.SockAddr.Type(); sockType {
case TypeIPv4:
i, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
}
if i > 32 {
return IfAddr{}, fmt.Errorf("parameter for operation %q on ipv4 addresses must be between 0 and 32", operation)
}
ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr)
ipv4Mask := net.CIDRMask(int(i), 32)
ipv4MaskUint32 := binary.BigEndian.Uint32(ipv4Mask)
maskedIpv4 := ipv4.NetIP().Mask(ipv4Mask)
maskedIpv4Uint32 := binary.BigEndian.Uint32(maskedIpv4)
maskedIpv4MaskUint32 := uint32(ipv4.Mask)
if ipv4MaskUint32 < maskedIpv4MaskUint32 {
maskedIpv4MaskUint32 = ipv4MaskUint32
}
return IfAddr{
SockAddr: IPv4Addr{
Address: IPv4Address(maskedIpv4Uint32),
Mask: IPv4Mask(maskedIpv4MaskUint32),
},
Interface: inputIfAddr.Interface,
}, nil
case TypeIPv6:
i, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err)
}
if i > 128 {
return IfAddr{}, fmt.Errorf("parameter for operation %q on ipv6 addresses must be between 0 and 64", operation)
}
ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr)
ipv6Mask := net.CIDRMask(int(i), 128)
ipv6MaskBigInt := new(big.Int)
ipv6MaskBigInt.SetBytes(ipv6Mask)
maskedIpv6 := ipv6.NetIP().Mask(ipv6Mask)
maskedIpv6BigInt := new(big.Int)
maskedIpv6BigInt.SetBytes(maskedIpv6)
maskedIpv6MaskBigInt := new(big.Int)
maskedIpv6MaskBigInt.Set(ipv6.Mask)
if ipv6MaskBigInt.Cmp(maskedIpv6MaskBigInt) == -1 {
maskedIpv6MaskBigInt = ipv6MaskBigInt
}
return IfAddr{
SockAddr: IPv6Addr{
Address: IPv6Address(maskedIpv6BigInt),
Mask: IPv6Mask(maskedIpv6MaskBigInt),
},
Interface: inputIfAddr.Interface,
}, nil
default:
return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType)
}
default:
return IfAddr{}, fmt.Errorf("unsupported math operation: %q", operation)
}
}
// IfAddrsMath will apply an IfAddrMath operation each IfAddr struct. Any
// failure will result in zero results.
func IfAddrsMath(operation, value string, inputIfAddrs IfAddrs) (IfAddrs, error) {
outputAddrs := make(IfAddrs, 0, len(inputIfAddrs))
for _, ifAddr := range inputIfAddrs {
result, err := IfAddrMath(operation, value, ifAddr)
if err != nil {
return IfAddrs{}, fmt.Errorf("unable to perform an IPMath operation on %s: %v", ifAddr, err)
}
outputAddrs = append(outputAddrs, result)
}
return outputAddrs, nil
}
// IncludeIfs returns an IfAddrs based on the passed in selector.
func IncludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
var includedIfs IfAddrs
@@ -736,6 +1044,10 @@ func SortIfBy(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
sortFuncs[i] = AscIfAddress
case "-address":
sortFuncs[i] = DescIfAddress
case "+default", "default":
sortFuncs[i] = AscIfDefault
case "-default":
sortFuncs[i] = DescIfDefault
case "+name", "name":
// The "name" selector returns an array of IfAddrs
// ordered by the interface name.
@@ -886,7 +1198,7 @@ func parseDefaultIfNameFromRoute(routeOut string) (string, error) {
// Linux.
func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) {
lines := strings.Split(routeOut, "\n")
re := regexp.MustCompile(`[\s]+`)
re := whitespaceRE.Copy()
for _, line := range lines {
kvs := re.Split(line, -1)
if len(kvs) < 5 {
@@ -929,7 +1241,7 @@ func parseDefaultIfNameWindows(routeOut, ipconfigOut string) (string, error) {
// support added.
func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
lines := strings.Split(routeOut, "\n")
re := regexp.MustCompile(`[\s]+`)
re := whitespaceRE.Copy()
for _, line := range lines {
kvs := re.Split(strings.TrimSpace(line), -1)
if len(kvs) < 3 {
@@ -949,17 +1261,17 @@ func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
// interface name forwarding traffic to the default gateway.
func parseDefaultIfNameWindowsIPConfig(defaultIPAddr, routeOut string) (string, error) {
lines := strings.Split(routeOut, "\n")
ifNameRE := regexp.MustCompile(`^Ethernet adapter ([^\s:]+):`)
ipAddrRE := regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
ifNameRe := ifNameRE.Copy()
ipAddrRe := ipAddrRE.Copy()
var ifName string
for _, line := range lines {
switch ifNameMatches := ifNameRE.FindStringSubmatch(line); {
switch ifNameMatches := ifNameRe.FindStringSubmatch(line); {
case len(ifNameMatches) > 1:
ifName = ifNameMatches[1]
continue
}
switch ipAddrMatches := ipAddrRE.FindStringSubmatch(line); {
switch ipAddrMatches := ipAddrRe.FindStringSubmatch(line); {
case len(ipAddrMatches) > 1 && ipAddrMatches[1] == defaultIPAddr:
return ifName, nil
}

View File

@@ -58,7 +58,8 @@ func NewIPv4Addr(ipv4Str string) (IPv4Addr, error) {
// Strip off any bogus hex-encoded netmasks that will be mis-parsed by Go. In
// particular, clients with the Barracuda VPN client will see something like:
// `192.168.3.51/00ffffff` as their IP address.
if match := trailingHexNetmaskRE.FindStringIndex(ipv4Str); match != nil {
trailingHexNetmaskRe := trailingHexNetmaskRE.Copy()
if match := trailingHexNetmaskRe.FindStringIndex(ipv4Str); match != nil {
ipv4Str = ipv4Str[:match[0]]
}

View File

@@ -3,6 +3,7 @@ package sockaddr
// ForwardingBlacklist is a faux RFC that includes a list of non-forwardable IP
// blocks.
const ForwardingBlacklist = 4294967295
const ForwardingBlacklistRFC = "4294967295"
// IsRFC tests to see if an SockAddr matches the specified RFC
func IsRFC(rfcNum uint, sa SockAddr) bool {

View File

@@ -5,10 +5,6 @@ import (
"os/exec"
)
var cmds map[string][]string = map[string][]string{
"ip": {"/sbin/ip", "route"},
}
type routeInfo struct {
cmds map[string][]string
}
@@ -16,15 +12,22 @@ type routeInfo struct {
// NewRouteInfo returns a Linux-specific implementation of the RouteInfo
// interface.
func NewRouteInfo() (routeInfo, error) {
// CoreOS Container Linux moved ip to /usr/bin/ip, so look it up on
// $PATH and fallback to /sbin/ip on error.
path, _ := exec.LookPath("ip")
if path == "" {
path = "/sbin/ip"
}
return routeInfo{
cmds: cmds,
cmds: map[string][]string{"ip": {path, "route"}},
}, nil
}
// GetDefaultInterfaceName returns the interface name attached to the default
// route on the default interface.
func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
out, err := exec.Command(cmds["ip"][0], cmds["ip"][1:]...).Output()
out, err := exec.Command(ri.cmds["ip"][0], ri.cmds["ip"][1:]...).Output()
if err != nil {
return "", err
}

View File

@@ -1,6 +1,7 @@
package sockaddr
import (
"encoding/json"
"fmt"
"strings"
)
@@ -176,3 +177,30 @@ func sockAddrInit() {
func SockAddrAttrs() []AttrName {
return sockAddrAttrs
}
// Although this is pretty trivial to do in a program, having the logic here is
// useful all around. Note that this marshals into a *string* -- the underlying
// string representation of the sockaddr. If you then unmarshal into this type
// in Go, all will work as expected, but externally you can take what comes out
// and use the string value directly.
type SockAddrMarshaler struct {
SockAddr
}
func (s *SockAddrMarshaler) MarshalJSON() ([]byte, error) {
return json.Marshal(s.SockAddr.String())
}
func (s *SockAddrMarshaler) UnmarshalJSON(in []byte) error {
var str string
err := json.Unmarshal(in, &str)
if err != nil {
return err
}
sa, err := NewSockAddr(str)
if err != nil {
return err
}
s.SockAddr = sa
return nil
}

View File

@@ -138,11 +138,11 @@ docker inspect myimage
##### Building a Dockerfile using [external frontend](https://hub.docker.com/r/tonistiigi/dockerfile/tags/):
During development, an external version of the Dockerfile frontend is pushed to https://hub.docker.com/r/tonistiigi/dockerfile that can be used with the gateway frontend. The source for the external frontend is currently located in `./frontend/dockerfile/cmd/dockerfile-frontend` but will move out of this repository in the future ([#163](https://github.com/moby/buildkit/issues/163)).
During development, an external version of the Dockerfile frontend is pushed to https://hub.docker.com/r/tonistiigi/dockerfile that can be used with the gateway frontend. The source for the external frontend is currently located in `./frontend/dockerfile/cmd/dockerfile-frontend` but will move out of this repository in the future ([#163](https://github.com/moby/buildkit/issues/163)). For automatic build from master branch of this repository `tonistiigi/dockerfile:master` image can be used.
```
buildctl build --frontend=gateway.v0 --frontend-opt=source=tonistiigi/dockerfile:v0 --local context=. --local dockerfile=.
buildctl build --frontend gateway.v0 --frontend-opt=source=tonistiigi/dockerfile:v0 --frontend-opt=context=git://github.com/moby/moby --frontend-opt build-arg:APT_MIRROR=cdn-fastly.deb.debian.org
buildctl build --frontend=gateway.v0 --frontend-opt=source=tonistiigi/dockerfile --local context=. --local dockerfile=.
buildctl build --frontend gateway.v0 --frontend-opt=source=tonistiigi/dockerfile --frontend-opt=context=git://github.com/moby/moby --frontend-opt build-arg:APT_MIRROR=cdn-fastly.deb.debian.org
````
### Exporters

View File

@@ -9,7 +9,6 @@ import (
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/docker/distribution/manifest"
v1 "github.com/moby/buildkit/cache/remotecache/v1"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/solver"
@@ -17,6 +16,7 @@ import (
"github.com/moby/buildkit/util/progress"
"github.com/moby/buildkit/util/push"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
@@ -46,7 +46,9 @@ func (ce *CacheExporter) Finalize(ctx context.Context, cc *v1.CacheChains, targe
// own type because oci type can't be pushed and docker type doesn't have annotations
type manifestList struct {
manifest.Versioned
specs.Versioned
MediaType string `json:"mediaType,omitempty"`
// Manifests references platform specific manifests.
Manifests []ocispec.Descriptor `json:"manifests"`

View File

@@ -2,27 +2,31 @@ package oci
import (
"context"
"errors"
"os"
"strconv"
"strings"
"github.com/containerd/containerd/containers"
containerdoci "github.com/containerd/containerd/oci"
"github.com/containerd/continuity/fs"
"github.com/opencontainers/runc/libcontainer/user"
"github.com/opencontainers/runtime-spec/specs-go"
)
func GetUser(ctx context.Context, root, username string) (uint32, uint32, error) {
func GetUser(ctx context.Context, root, username string) (uint32, uint32, []uint32, error) {
// fast path from uid/gid
if uid, gid, err := ParseUser(username); err == nil {
return uid, gid, nil
if uid, gid, err := ParseUIDGID(username); err == nil {
return uid, gid, nil, nil
}
passwdPath, err := user.GetPasswdPath()
if err != nil {
return 0, 0, err
return 0, 0, nil, err
}
groupPath, err := user.GetGroupPath()
if err != nil {
return 0, 0, err
return 0, 0, nil, err
}
passwdFile, err := openUserFile(root, passwdPath)
if err == nil {
@@ -35,33 +39,29 @@ func GetUser(ctx context.Context, root, username string) (uint32, uint32, error)
execUser, err := user.GetExecUser(username, nil, passwdFile, groupFile)
if err != nil {
return 0, 0, err
return 0, 0, nil, err
}
return uint32(execUser.Uid), uint32(execUser.Gid), nil
var sgids []uint32
for _, g := range execUser.Sgids {
sgids = append(sgids, uint32(g))
}
return uint32(execUser.Uid), uint32(execUser.Gid), sgids, nil
}
func ParseUser(str string) (uid uint32, gid uint32, err error) {
// ParseUIDGID takes the fast path to parse UID and GID if and only if they are both provided
func ParseUIDGID(str string) (uid uint32, gid uint32, err error) {
if str == "" {
return 0, 0, nil
}
parts := strings.SplitN(str, ":", 2)
for i, v := range parts {
switch i {
case 0:
uid, err = parseUID(v)
if err != nil {
return 0, 0, err
}
if len(parts) == 1 {
gid = uid
}
case 1:
gid, err = parseUID(v)
if err != nil {
return 0, 0, err
}
}
if len(parts) == 1 {
return 0, 0, errors.New("groups ID is not provided")
}
if uid, err = parseUID(parts[0]); err != nil {
return 0, 0, err
}
if gid, err = parseUID(parts[1]); err != nil {
return 0, 0, err
}
return
}
@@ -84,3 +84,24 @@ func parseUID(str string) (uint32, error) {
}
return uint32(uid), nil
}
// WithUIDGID allows the UID and GID for the Process to be set
// FIXME: This is a temporeray fix for the missing supplementary GIDs from containerd
// once the PR in containerd is merged we should remove this function.
func WithUIDGID(uid, gid uint32, sgids []uint32) containerdoci.SpecOpts {
return func(_ context.Context, _ containerdoci.Client, _ *containers.Container, s *containerdoci.Spec) error {
setProcess(s)
s.Process.User.UID = uid
s.Process.User.GID = gid
s.Process.User.AdditionalGids = sgids
return nil
}
}
// setProcess sets Process to empty if unset
// FIXME: Same on this one. Need to be removed after containerd fix merged
func setProcess(s *containerdoci.Spec) {
if s.Process == nil {
s.Process = &specs.Process{}
}
}

View File

@@ -133,7 +133,7 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.
}
defer mount.Unmount(rootFSPath, 0)
uid, gid, err := oci.GetUser(ctx, rootFSPath, meta.User)
uid, gid, sgids, err := oci.GetUser(ctx, rootFSPath, meta.User)
if err != nil {
return err
}
@@ -143,7 +143,7 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.
return err
}
defer f.Close()
opts := []containerdoci.SpecOpts{containerdoci.WithUIDGID(uid, gid)}
opts := []containerdoci.SpecOpts{oci.WithUIDGID(uid, gid, sgids)}
if system.SeccompSupported() {
opts = append(opts, seccomp.WithDefaultProfile())
}
@@ -170,9 +170,7 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.
}
if w.rootless {
specconv.ToRootless(spec, &specconv.RootlessOpts{
MapSubUIDGID: true,
})
specconv.ToRootless(spec, nil)
// TODO(AkihiroSuda): keep Cgroups enabled if /sys/fs/cgroup/cpuset/buildkit exists and writable
spec.Linux.CgroupsPath = ""
// TODO(AkihiroSuda): ToRootless removes netns, but we should readd netns here

View File

@@ -91,8 +91,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
metaResolver = imagemetaresolver.Default()
}
var allDispatchStates []*dispatchState
dispatchStatesByName := map[string]*dispatchState{}
allDispatchStates := newDispatchStates()
// set base state for every image
for _, st := range stages {
@@ -100,6 +99,9 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
if err != nil {
return nil, nil, err
}
if name == "" {
return nil, nil, errors.Errorf("base name (%s) should not be blank", st.BaseName)
}
st.BaseName = name
ds := &dispatchState{
@@ -121,13 +123,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
ds.platform = &p
}
if d, ok := dispatchStatesByName[st.BaseName]; ok {
ds.base = d
}
allDispatchStates = append(allDispatchStates, ds)
if st.Name != "" {
dispatchStatesByName[strings.ToLower(st.Name)] = ds
}
allDispatchStates.addState(ds)
if opt.IgnoreCache != nil {
if len(opt.IgnoreCache) == 0 {
ds.ignoreCache = true
@@ -143,20 +139,20 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
var target *dispatchState
if opt.Target == "" {
target = allDispatchStates[len(allDispatchStates)-1]
target = allDispatchStates.lastTarget()
} else {
var ok bool
target, ok = dispatchStatesByName[strings.ToLower(opt.Target)]
target, ok = allDispatchStates.findStateByName(opt.Target)
if !ok {
return nil, nil, errors.Errorf("target stage %s could not be found", opt.Target)
}
}
// fill dependencies to stages so unreachable ones can avoid loading image configs
for _, d := range allDispatchStates {
for _, d := range allDispatchStates.states {
d.commands = make([]command, len(d.stage.Commands))
for i, cmd := range d.stage.Commands {
newCmd, err := toCommand(cmd, dispatchStatesByName, allDispatchStates)
newCmd, err := toCommand(cmd, allDispatchStates)
if err != nil {
return nil, nil, err
}
@@ -165,7 +161,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
if src != nil {
d.deps[src] = struct{}{}
if src.unregistered {
allDispatchStates = append(allDispatchStates, src)
allDispatchStates.addState(src)
}
}
}
@@ -173,7 +169,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
}
eg, ctx := errgroup.WithContext(ctx)
for i, d := range allDispatchStates {
for i, d := range allDispatchStates.states {
reachable := isReachable(target, d)
// resolve image config for every stage
if d.base == nil {
@@ -239,7 +235,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
buildContext := &mutableOutput{}
ctxPaths := map[string]struct{}{}
for _, d := range allDispatchStates {
for _, d := range allDispatchStates.states {
if !isReachable(target, d) {
continue
}
@@ -271,17 +267,16 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
}
opt := dispatchOpt{
allDispatchStates: allDispatchStates,
dispatchStatesByName: dispatchStatesByName,
metaArgs: metaArgs,
buildArgValues: opt.BuildArgs,
shlex: shlex,
sessionID: opt.SessionID,
buildContext: llb.NewState(buildContext),
proxyEnv: proxyEnv,
cacheIDNamespace: opt.CacheIDNamespace,
buildPlatforms: opt.BuildPlatforms,
targetPlatform: *opt.TargetPlatform,
allDispatchStates: allDispatchStates,
metaArgs: metaArgs,
buildArgValues: opt.BuildArgs,
shlex: shlex,
sessionID: opt.SessionID,
buildContext: llb.NewState(buildContext),
proxyEnv: proxyEnv,
cacheIDNamespace: opt.CacheIDNamespace,
buildPlatforms: opt.BuildPlatforms,
targetPlatform: *opt.TargetPlatform,
}
if err = dispatchOnBuild(d, d.image.Config.OnBuild, opt); err != nil {
@@ -330,14 +325,14 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
return &st, &target.image, nil
}
func toCommand(ic instructions.Command, dispatchStatesByName map[string]*dispatchState, allDispatchStates []*dispatchState) (command, error) {
func toCommand(ic instructions.Command, allDispatchStates *dispatchStates) (command, error) {
cmd := command{Command: ic}
if c, ok := ic.(*instructions.CopyCommand); ok {
if c.From != "" {
var stn *dispatchState
index, err := strconv.Atoi(c.From)
if err != nil {
stn, ok = dispatchStatesByName[strings.ToLower(c.From)]
stn, ok = allDispatchStates.findStateByName(c.From)
if !ok {
stn = &dispatchState{
stage: instructions.Stage{BaseName: c.From},
@@ -346,16 +341,16 @@ func toCommand(ic instructions.Command, dispatchStatesByName map[string]*dispatc
}
}
} else {
if index < 0 || index >= len(allDispatchStates) {
return command{}, errors.Errorf("invalid stage index %d", index)
stn, err = allDispatchStates.findStateByIndex(index)
if err != nil {
return command{}, err
}
stn = allDispatchStates[index]
}
cmd.sources = []*dispatchState{stn}
}
}
if ok := detectRunMount(&cmd, dispatchStatesByName, allDispatchStates); ok {
if ok := detectRunMount(&cmd, allDispatchStates); ok {
return cmd, nil
}
@@ -363,17 +358,16 @@ func toCommand(ic instructions.Command, dispatchStatesByName map[string]*dispatc
}
type dispatchOpt struct {
allDispatchStates []*dispatchState
dispatchStatesByName map[string]*dispatchState
metaArgs []instructions.ArgCommand
buildArgValues map[string]string
shlex *shell.Lex
sessionID string
buildContext llb.State
proxyEnv *llb.ProxyEnv
cacheIDNamespace string
targetPlatform specs.Platform
buildPlatforms []specs.Platform
allDispatchStates *dispatchStates
metaArgs []instructions.ArgCommand
buildArgValues map[string]string
shlex *shell.Lex
sessionID string
buildContext llb.State
proxyEnv *llb.ProxyEnv
cacheIDNamespace string
targetPlatform specs.Platform
buildPlatforms []specs.Platform
}
func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
@@ -456,6 +450,43 @@ type dispatchState struct {
unregistered bool
}
type dispatchStates struct {
states []*dispatchState
statesByName map[string]*dispatchState
}
func newDispatchStates() *dispatchStates {
return &dispatchStates{statesByName: map[string]*dispatchState{}}
}
func (dss *dispatchStates) addState(ds *dispatchState) {
dss.states = append(dss.states, ds)
if d, ok := dss.statesByName[ds.stage.BaseName]; ok {
ds.base = d
}
if ds.stage.Name != "" {
dss.statesByName[strings.ToLower(ds.stage.Name)] = ds
}
}
func (dss *dispatchStates) findStateByName(name string) (*dispatchState, bool) {
ds, ok := dss.statesByName[strings.ToLower(name)]
return ds, ok
}
func (dss *dispatchStates) findStateByIndex(index int) (*dispatchState, error) {
if index < 0 || index >= len(dss.states) {
return nil, errors.Errorf("invalid stage index %d", index)
}
return dss.states[index], nil
}
func (dss *dispatchStates) lastTarget() *dispatchState {
return dss.states[len(dss.states)-1]
}
type command struct {
instructions.Command
sources []*dispatchState
@@ -474,7 +505,7 @@ func dispatchOnBuild(d *dispatchState, triggers []string, opt dispatchOpt) error
if err != nil {
return err
}
cmd, err := toCommand(ic, opt.dispatchStatesByName, opt.allDispatchStates)
cmd, err := toCommand(ic, opt.allDispatchStates)
if err != nil {
return err
}
@@ -570,7 +601,11 @@ func dispatchCopy(d *dispatchState, c instructions.SourcesAndDest, sourceState l
for i, src := range c.Sources() {
commitMessage.WriteString(" " + src)
if isAddCommand && (strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://")) {
if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
if !isAddCommand {
return errors.New("source can't be a URL for COPY")
}
// Resources from remote URLs are not decompressed.
// https://docs.docker.com/engine/reference/builder/#add
//

View File

@@ -7,7 +7,7 @@ import (
"github.com/moby/buildkit/frontend/dockerfile/instructions"
)
func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState, allDispatchStates []*dispatchState) bool {
func detectRunMount(cmd *command, allDispatchStates *dispatchStates) bool {
return false
}

View File

@@ -5,14 +5,13 @@ package dockerfile2llb
import (
"path"
"path/filepath"
"strings"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/pkg/errors"
)
func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState, allDispatchStates []*dispatchState) bool {
func detectRunMount(cmd *command, allDispatchStates *dispatchStates) bool {
if c, ok := cmd.Command.(*instructions.RunCommand); ok {
mounts := instructions.GetMounts(c)
sources := make([]*dispatchState, len(mounts))
@@ -24,7 +23,7 @@ func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState
if from == "" || mount.Type == instructions.MountTypeTmpfs {
continue
}
stn, ok := dispatchStatesByName[strings.ToLower(from)]
stn, ok := allDispatchStates.findStateByName(from)
if !ok {
stn = &dispatchState{
stage: instructions.Stage{BaseName: from},

View File

@@ -72,7 +72,7 @@ func (bf *BFlags) AddString(name string, def string) *Flag {
return flag
}
// AddString adds a string flag to BFlags that can match multiple values
// AddStrings adds a string flag to BFlags that can match multiple values
func (bf *BFlags) AddStrings(name string) *Flag {
flag := bf.addFlag(name, stringsType)
if flag == nil {

View File

@@ -159,7 +159,7 @@ func (s SourcesAndDest) Dest() string {
// AddCommand : ADD foo /path
//
// Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
// Add the file 'foo' to '/path'. Tarball and Remote URL (http, https) handling
// exist here. If you do not wish to have this automatic handling, use COPY.
//
type AddCommand struct {

View File

@@ -2,8 +2,9 @@
package shell
// EqualEnvKeys compare two strings and returns true if they are equal. On
// Windows this comparison is case insensitive.
// EqualEnvKeys compare two strings and returns true if they are equal.
// On Unix this comparison is case sensitive.
// On Windows this comparison is case insensitive.
func EqualEnvKeys(from, to string) bool {
return from == to
}

View File

@@ -2,8 +2,9 @@ package shell
import "strings"
// EqualEnvKeys compare two strings and returns true if they are equal. On
// Windows this comparison is case insensitive.
// EqualEnvKeys compare two strings and returns true if they are equal.
// On Unix this comparison is case sensitive.
// On Windows this comparison is case insensitive.
func EqualEnvKeys(from, to string) bool {
return strings.ToUpper(from) == strings.ToUpper(to)
}

View File

@@ -141,13 +141,17 @@ func DetectManifestMediaType(ra content.ReaderAt) (string, error) {
}
var mfst struct {
Config json.RawMessage `json:"config"`
MediaType string `json:"mediaType"`
Config json.RawMessage `json:"config"`
}
if err := json.Unmarshal(p, &mfst); err != nil {
return "", err
}
if mfst.MediaType != "" {
return mfst.MediaType, nil
}
if mfst.Config != nil {
return images.MediaTypeDockerSchema2Manifest, nil
}